wp/wp-admin/js/updates.js
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
     9 
     9 
    10 /**
    10 /**
    11  * @param {jQuery}  $                                   jQuery object.
    11  * @param {jQuery}  $                                   jQuery object.
    12  * @param {object}  wp                                  WP object.
    12  * @param {object}  wp                                  WP object.
    13  * @param {object}  settings                            WP Updates settings.
    13  * @param {object}  settings                            WP Updates settings.
    14  * @param {string}  settings.ajax_nonce                 AJAX nonce.
    14  * @param {string}  settings.ajax_nonce                 Ajax nonce.
    15  * @param {object}  settings.l10n                       Translation strings.
       
    16  * @param {object=} settings.plugins                    Base names of plugins in their different states.
    15  * @param {object=} settings.plugins                    Base names of plugins in their different states.
    17  * @param {Array}   settings.plugins.all                Base names of all plugins.
    16  * @param {Array}   settings.plugins.all                Base names of all plugins.
    18  * @param {Array}   settings.plugins.active             Base names of active plugins.
    17  * @param {Array}   settings.plugins.active             Base names of active plugins.
    19  * @param {Array}   settings.plugins.inactive           Base names of inactive plugins.
    18  * @param {Array}   settings.plugins.inactive           Base names of inactive plugins.
    20  * @param {Array}   settings.plugins.upgrade            Base names of plugins with updates available.
    19  * @param {Array}   settings.plugins.upgrade            Base names of plugins with updates available.
    25  * @param {number}  settings.themes.disabled            Amount of disabled themes.
    24  * @param {number}  settings.themes.disabled            Amount of disabled themes.
    26  * @param {object=} settings.totals                     Combined information for available update counts.
    25  * @param {object=} settings.totals                     Combined information for available update counts.
    27  * @param {number}  settings.totals.count               Holds the amount of available updates.
    26  * @param {number}  settings.totals.count               Holds the amount of available updates.
    28  */
    27  */
    29 (function( $, wp, settings ) {
    28 (function( $, wp, settings ) {
    30 	var $document = $( document );
    29 	var $document = $( document ),
       
    30 		__ = wp.i18n.__,
       
    31 		_x = wp.i18n._x,
       
    32 		sprintf = wp.i18n.sprintf;
    31 
    33 
    32 	wp = wp || {};
    34 	wp = wp || {};
    33 
    35 
    34 	/**
    36 	/**
    35 	 * The WP Updates object.
    37 	 * The WP Updates object.
    39 	 * @namespace wp.updates
    41 	 * @namespace wp.updates
    40 	 */
    42 	 */
    41 	wp.updates = {};
    43 	wp.updates = {};
    42 
    44 
    43 	/**
    45 	/**
       
    46 	 * Removed in 5.5.0, needed for back-compatibility.
       
    47 	 *
       
    48 	 * @since 4.2.0
       
    49 	 * @deprecated 5.5.0
       
    50 	 *
       
    51 	 * @type {object}
       
    52 	 */
       
    53 	wp.updates.l10n = {
       
    54 		searchResults: '',
       
    55 		searchResultsLabel: '',
       
    56 		noPlugins: '',
       
    57 		noItemsSelected: '',
       
    58 		updating: '',
       
    59 		pluginUpdated: '',
       
    60 		themeUpdated: '',
       
    61 		update: '',
       
    62 		updateNow: '',
       
    63 		pluginUpdateNowLabel: '',
       
    64 		updateFailedShort: '',
       
    65 		updateFailed: '',
       
    66 		pluginUpdatingLabel: '',
       
    67 		pluginUpdatedLabel: '',
       
    68 		pluginUpdateFailedLabel: '',
       
    69 		updatingMsg: '',
       
    70 		updatedMsg: '',
       
    71 		updateCancel: '',
       
    72 		beforeunload: '',
       
    73 		installNow: '',
       
    74 		pluginInstallNowLabel: '',
       
    75 		installing: '',
       
    76 		pluginInstalled: '',
       
    77 		themeInstalled: '',
       
    78 		installFailedShort: '',
       
    79 		installFailed: '',
       
    80 		pluginInstallingLabel: '',
       
    81 		themeInstallingLabel: '',
       
    82 		pluginInstalledLabel: '',
       
    83 		themeInstalledLabel: '',
       
    84 		pluginInstallFailedLabel: '',
       
    85 		themeInstallFailedLabel: '',
       
    86 		installingMsg: '',
       
    87 		installedMsg: '',
       
    88 		importerInstalledMsg: '',
       
    89 		aysDelete: '',
       
    90 		aysDeleteUninstall: '',
       
    91 		aysBulkDelete: '',
       
    92 		aysBulkDeleteThemes: '',
       
    93 		deleting: '',
       
    94 		deleteFailed: '',
       
    95 		pluginDeleted: '',
       
    96 		themeDeleted: '',
       
    97 		livePreview: '',
       
    98 		activatePlugin: '',
       
    99 		activateTheme: '',
       
   100 		activatePluginLabel: '',
       
   101 		activateThemeLabel: '',
       
   102 		activateImporter: '',
       
   103 		activateImporterLabel: '',
       
   104 		unknownError: '',
       
   105 		connectionError: '',
       
   106 		nonceError: '',
       
   107 		pluginsFound: '',
       
   108 		noPluginsFound: '',
       
   109 		autoUpdatesEnable: '',
       
   110 		autoUpdatesEnabling: '',
       
   111 		autoUpdatesEnabled: '',
       
   112 		autoUpdatesDisable: '',
       
   113 		autoUpdatesDisabling: '',
       
   114 		autoUpdatesDisabled: '',
       
   115 		autoUpdatesError: ''
       
   116 	};
       
   117 
       
   118 	wp.updates.l10n = window.wp.deprecateL10nObject( 'wp.updates.l10n', wp.updates.l10n );
       
   119 
       
   120 	/**
    44 	 * User nonce for ajax calls.
   121 	 * User nonce for ajax calls.
    45 	 *
   122 	 *
    46 	 * @since 4.2.0
   123 	 * @since 4.2.0
    47 	 *
   124 	 *
    48 	 * @type {string}
   125 	 * @type {string}
    49 	 */
   126 	 */
    50 	wp.updates.ajaxNonce = settings.ajax_nonce;
   127 	wp.updates.ajaxNonce = settings.ajax_nonce;
    51 
       
    52 	/**
       
    53 	 * Localized strings.
       
    54 	 *
       
    55 	 * @since 4.2.0
       
    56 	 *
       
    57 	 * @type {object}
       
    58 	 */
       
    59 	wp.updates.l10n = settings.l10n;
       
    60 
   128 
    61 	/**
   129 	/**
    62 	 * Current search term.
   130 	 * Current search term.
    63 	 *
   131 	 *
    64 	 * @since 4.6.0
   132 	 * @since 4.6.0
   155 	/**
   223 	/**
   156 	 * Adds or updates an admin notice.
   224 	 * Adds or updates an admin notice.
   157 	 *
   225 	 *
   158 	 * @since 4.6.0
   226 	 * @since 4.6.0
   159 	 *
   227 	 *
   160 	 * @param {object}  data
   228 	 * @param {Object}  data
   161 	 * @param {*=}      data.selector      Optional. Selector of an element to be replaced with the admin notice.
   229 	 * @param {*=}      data.selector      Optional. Selector of an element to be replaced with the admin notice.
   162 	 * @param {string=} data.id            Optional. Unique id that will be used as the notice's id attribute.
   230 	 * @param {string=} data.id            Optional. Unique id that will be used as the notice's id attribute.
   163 	 * @param {string=} data.className     Optional. Class names that will be used in the admin notice.
   231 	 * @param {string=} data.className     Optional. Class names that will be used in the admin notice.
   164 	 * @param {string=} data.message       Optional. The message displayed in the notice.
   232 	 * @param {string=} data.message       Optional. The message displayed in the notice.
   165 	 * @param {number=} data.successes     Optional. The amount of successful operations.
   233 	 * @param {number=} data.successes     Optional. The amount of successful operations.
   199 	 * Handles Ajax requests to WordPress.
   267 	 * Handles Ajax requests to WordPress.
   200 	 *
   268 	 *
   201 	 * @since 4.6.0
   269 	 * @since 4.6.0
   202 	 *
   270 	 *
   203 	 * @param {string} action The type of Ajax request ('update-plugin', 'install-theme', etc).
   271 	 * @param {string} action The type of Ajax request ('update-plugin', 'install-theme', etc).
   204 	 * @param {object} data   Data that needs to be passed to the ajax callback.
   272 	 * @param {Object} data   Data that needs to be passed to the ajax callback.
   205 	 * @return {$.promise}    A jQuery promise that represents the request,
   273 	 * @return {$.promise}    A jQuery promise that represents the request,
   206 	 *                        decorated with an abort() method.
   274 	 *                        decorated with an abort() method.
   207 	 */
   275 	 */
   208 	wp.updates.ajax = function( action, data ) {
   276 	wp.updates.ajax = function( action, data ) {
   209 		var options = {};
   277 		var options = {};
   248 	/**
   316 	/**
   249 	 * Actions performed after every Ajax request.
   317 	 * Actions performed after every Ajax request.
   250 	 *
   318 	 *
   251 	 * @since 4.6.0
   319 	 * @since 4.6.0
   252 	 *
   320 	 *
   253 	 * @param {object}  response
   321 	 * @param {Object}  response
   254 	 * @param {array=}  response.debug     Optional. Debug information.
   322 	 * @param {Array=}  response.debug     Optional. Debug information.
   255 	 * @param {string=} response.errorCode Optional. Error code for an error that occurred.
   323 	 * @param {string=} response.errorCode Optional. Error code for an error that occurred.
   256 	 */
   324 	 */
   257 	wp.updates.ajaxAlways = function( response ) {
   325 	wp.updates.ajaxAlways = function( response ) {
   258 		if ( ! response.errorCode || 'unable_to_connect_to_filesystem' !== response.errorCode ) {
   326 		if ( ! response.errorCode || 'unable_to_connect_to_filesystem' !== response.errorCode ) {
   259 			wp.updates.ajaxLocked = false;
   327 			wp.updates.ajaxLocked = false;
   360 	 * Sends an Ajax request to the server to update a plugin.
   428 	 * Sends an Ajax request to the server to update a plugin.
   361 	 *
   429 	 *
   362 	 * @since 4.2.0
   430 	 * @since 4.2.0
   363 	 * @since 4.6.0 More accurately named `updatePlugin`.
   431 	 * @since 4.6.0 More accurately named `updatePlugin`.
   364 	 *
   432 	 *
   365 	 * @param {object}               args         Arguments.
   433 	 * @param {Object}               args         Arguments.
   366 	 * @param {string}               args.plugin  Plugin basename.
   434 	 * @param {string}               args.plugin  Plugin basename.
   367 	 * @param {string}               args.slug    Plugin slug.
   435 	 * @param {string}               args.slug    Plugin slug.
   368 	 * @param {updatePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.updatePluginSuccess
   436 	 * @param {updatePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.updatePluginSuccess
   369 	 * @param {updatePluginError=}   args.error   Optional. Error callback. Default: wp.updates.updatePluginError
   437 	 * @param {updatePluginError=}   args.error   Optional. Error callback. Default: wp.updates.updatePluginError
   370 	 * @return {$.promise} A jQuery promise that represents the request,
   438 	 * @return {$.promise} A jQuery promise that represents the request,
   379 		}, args );
   447 		}, args );
   380 
   448 
   381 		if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
   449 		if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
   382 			$updateRow = $( 'tr[data-plugin="' + args.plugin + '"]' );
   450 			$updateRow = $( 'tr[data-plugin="' + args.plugin + '"]' );
   383 			$message   = $updateRow.find( '.update-message' ).removeClass( 'notice-error' ).addClass( 'updating-message notice-warning' ).find( 'p' );
   451 			$message   = $updateRow.find( '.update-message' ).removeClass( 'notice-error' ).addClass( 'updating-message notice-warning' ).find( 'p' );
   384 			message    = wp.updates.l10n.pluginUpdatingLabel.replace( '%s', $updateRow.find( '.plugin-title strong' ).text() );
   452 			message    = sprintf(
       
   453 				/* translators: %s: Plugin name and version. */
       
   454  				_x( 'Updating %s...', 'plugin' ),
       
   455 				$updateRow.find( '.plugin-title strong' ).text()
       
   456 			);
   385 		} else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
   457 		} else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
   386 			$card    = $( '.plugin-card-' + args.slug );
   458 			$card    = $( '.plugin-card-' + args.slug );
   387 			$message = $card.find( '.update-now' ).addClass( 'updating-message' );
   459 			$message = $card.find( '.update-now' ).addClass( 'updating-message' );
   388 			message  = wp.updates.l10n.pluginUpdatingLabel.replace( '%s', $message.data( 'name' ) );
   460 			message    = sprintf(
       
   461 				/* translators: %s: Plugin name and version. */
       
   462  				_x( 'Updating %s...', 'plugin' ),
       
   463 				$message.data( 'name' )
       
   464 			);
   389 
   465 
   390 			// Remove previous error messages, if any.
   466 			// Remove previous error messages, if any.
   391 			$card.removeClass( 'plugin-card-update-failed' ).find( '.notice.notice-error' ).remove();
   467 			$card.removeClass( 'plugin-card-update-failed' ).find( '.notice.notice-error' ).remove();
   392 		}
   468 		}
   393 
   469 
   394 		if ( $message.html() !== wp.updates.l10n.updating ) {
   470 		if ( $message.html() !== __( 'Updating...' ) ) {
   395 			$message.data( 'originaltext', $message.html() );
   471 			$message.data( 'originaltext', $message.html() );
   396 		}
   472 		}
   397 
   473 
   398 		$message
   474 		$message
   399 			.attr( 'aria-label', message )
   475 			.attr( 'aria-label', message )
   400 			.text( wp.updates.l10n.updating );
   476 			.text( __( 'Updating...' ) );
   401 
   477 
   402 		$document.trigger( 'wp-plugin-updating', args );
   478 		$document.trigger( 'wp-plugin-updating', args );
   403 
   479 
   404 		return wp.updates.ajax( 'update-plugin', args );
   480 		return wp.updates.ajax( 'update-plugin', args );
   405 	};
   481 	};
   407 	/**
   483 	/**
   408 	 * Updates the UI appropriately after a successful plugin update.
   484 	 * Updates the UI appropriately after a successful plugin update.
   409 	 *
   485 	 *
   410 	 * @since 4.2.0
   486 	 * @since 4.2.0
   411 	 * @since 4.6.0 More accurately named `updatePluginSuccess`.
   487 	 * @since 4.6.0 More accurately named `updatePluginSuccess`.
   412 	 *
   488 	 * @since 5.5.0 Auto-update "time to next update" text cleared.
   413 	 * @param {object} response            Response from the server.
   489 	 *
       
   490 	 * @param {Object} response            Response from the server.
   414 	 * @param {string} response.slug       Slug of the plugin to be updated.
   491 	 * @param {string} response.slug       Slug of the plugin to be updated.
   415 	 * @param {string} response.plugin     Basename of the plugin to be updated.
   492 	 * @param {string} response.plugin     Basename of the plugin to be updated.
   416 	 * @param {string} response.pluginName Name of the plugin to be updated.
   493 	 * @param {string} response.pluginName Name of the plugin to be updated.
   417 	 * @param {string} response.oldVersion Old version of the plugin.
   494 	 * @param {string} response.oldVersion Old version of the plugin.
   418 	 * @param {string} response.newVersion New version of the plugin.
   495 	 * @param {string} response.newVersion New version of the plugin.
   429 				.addClass( 'updated-message notice-success' ).find( 'p' );
   506 				.addClass( 'updated-message notice-success' ).find( 'p' );
   430 
   507 
   431 			// Update the version number in the row.
   508 			// Update the version number in the row.
   432 			newText = $pluginRow.find( '.plugin-version-author-uri' ).html().replace( response.oldVersion, response.newVersion );
   509 			newText = $pluginRow.find( '.plugin-version-author-uri' ).html().replace( response.oldVersion, response.newVersion );
   433 			$pluginRow.find( '.plugin-version-author-uri' ).html( newText );
   510 			$pluginRow.find( '.plugin-version-author-uri' ).html( newText );
       
   511 
       
   512 			// Clear the "time to next auto-update" text.
       
   513 			$pluginRow.find( '.auto-update-time' ).empty();
   434 		} else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
   514 		} else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
   435 			$updateMessage = $( '.plugin-card-' + response.slug ).find( '.update-now' )
   515 			$updateMessage = $( '.plugin-card-' + response.slug ).find( '.update-now' )
   436 				.removeClass( 'updating-message' )
   516 				.removeClass( 'updating-message' )
   437 				.addClass( 'button-disabled updated-message' );
   517 				.addClass( 'button-disabled updated-message' );
   438 		}
   518 		}
   439 
   519 
   440 		$updateMessage
   520 		$updateMessage
   441 			.attr( 'aria-label', wp.updates.l10n.pluginUpdatedLabel.replace( '%s', response.pluginName ) )
   521 			.attr(
   442 			.text( wp.updates.l10n.pluginUpdated );
   522 				'aria-label',
   443 
   523 				sprintf(
   444 		wp.a11y.speak( wp.updates.l10n.updatedMsg, 'polite' );
   524 					/* translators: %s: Plugin name and version. */
       
   525 					_x( '%s updated!', 'plugin' ),
       
   526 					response.pluginName
       
   527 				)
       
   528 			)
       
   529 			.text( _x( 'Updated!', 'plugin' ) );
       
   530 
       
   531 		wp.a11y.speak( __( 'Update completed successfully.' ) );
   445 
   532 
   446 		wp.updates.decrementCount( 'plugin' );
   533 		wp.updates.decrementCount( 'plugin' );
   447 
   534 
   448 		$document.trigger( 'wp-plugin-update-success', response );
   535 		$document.trigger( 'wp-plugin-update-success', response );
   449 	};
   536 	};
   452 	 * Updates the UI appropriately after a failed plugin update.
   539 	 * Updates the UI appropriately after a failed plugin update.
   453 	 *
   540 	 *
   454 	 * @since 4.2.0
   541 	 * @since 4.2.0
   455 	 * @since 4.6.0 More accurately named `updatePluginError`.
   542 	 * @since 4.6.0 More accurately named `updatePluginError`.
   456 	 *
   543 	 *
   457 	 * @param {object}  response              Response from the server.
   544 	 * @param {Object}  response              Response from the server.
   458 	 * @param {string}  response.slug         Slug of the plugin to be updated.
   545 	 * @param {string}  response.slug         Slug of the plugin to be updated.
   459 	 * @param {string}  response.plugin       Basename of the plugin to be updated.
   546 	 * @param {string}  response.plugin       Basename of the plugin to be updated.
   460 	 * @param {string=} response.pluginName   Optional. Name of the plugin to be updated.
   547 	 * @param {string=} response.pluginName   Optional. Name of the plugin to be updated.
   461 	 * @param {string}  response.errorCode    Error code for the error that occurred.
   548 	 * @param {string}  response.errorCode    Error code for the error that occurred.
   462 	 * @param {string}  response.errorMessage The error that occurred.
   549 	 * @param {string}  response.errorMessage The error that occurred.
   470 
   557 
   471 		if ( wp.updates.maybeHandleCredentialError( response, 'update-plugin' ) ) {
   558 		if ( wp.updates.maybeHandleCredentialError( response, 'update-plugin' ) ) {
   472 			return;
   559 			return;
   473 		}
   560 		}
   474 
   561 
   475 		errorMessage = wp.updates.l10n.updateFailed.replace( '%s', response.errorMessage );
   562 		errorMessage = sprintf(
       
   563 			/* translators: %s: Error string for a failed update. */
       
   564 			__( 'Update failed: %s' ),
       
   565 			response.errorMessage
       
   566 		);
   476 
   567 
   477 		if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
   568 		if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
   478 			if ( response.plugin ) {
   569 			if ( response.plugin ) {
   479 				$message = $( 'tr[data-plugin="' + response.plugin + '"]' ).find( '.update-message' );
   570 				$message = $( 'tr[data-plugin="' + response.plugin + '"]' ).find( '.update-message' );
   480 			} else {
   571 			} else {
   482 			}
   573 			}
   483 			$message.removeClass( 'updating-message notice-warning' ).addClass( 'notice-error' ).find( 'p' ).html( errorMessage );
   574 			$message.removeClass( 'updating-message notice-warning' ).addClass( 'notice-error' ).find( 'p' ).html( errorMessage );
   484 
   575 
   485 			if ( response.pluginName ) {
   576 			if ( response.pluginName ) {
   486 				$message.find( 'p' )
   577 				$message.find( 'p' )
   487 					.attr( 'aria-label', wp.updates.l10n.pluginUpdateFailedLabel.replace( '%s', response.pluginName ) );
   578 					.attr(
       
   579 						'aria-label',
       
   580 						sprintf(
       
   581 							/* translators: %s: Plugin name and version. */
       
   582 							_x( '%s update failed.', 'plugin' ),
       
   583 							response.pluginName
       
   584 						)
       
   585 					);
   488 			} else {
   586 			} else {
   489 				$message.find( 'p' ).removeAttr( 'aria-label' );
   587 				$message.find( 'p' ).removeAttr( 'aria-label' );
   490 			}
   588 			}
   491 		} else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
   589 		} else if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
   492 			$card = $( '.plugin-card-' + response.slug )
   590 			$card = $( '.plugin-card-' + response.slug )
   495 					className: 'update-message notice-error notice-alt is-dismissible',
   593 					className: 'update-message notice-error notice-alt is-dismissible',
   496 					message:   errorMessage
   594 					message:   errorMessage
   497 				} ) );
   595 				} ) );
   498 
   596 
   499 			$card.find( '.update-now' )
   597 			$card.find( '.update-now' )
   500 				.text( wp.updates.l10n.updateFailedShort ).removeClass( 'updating-message' );
   598 				.text(  __( 'Update failed.' ) )
       
   599 				.removeClass( 'updating-message' );
   501 
   600 
   502 			if ( response.pluginName ) {
   601 			if ( response.pluginName ) {
   503 				$card.find( '.update-now' )
   602 				$card.find( '.update-now' )
   504 					.attr( 'aria-label', wp.updates.l10n.pluginUpdateFailedLabel.replace( '%s', response.pluginName ) );
   603 					.attr(
       
   604 						'aria-label',
       
   605 						sprintf(
       
   606 							/* translators: %s: Plugin name and version. */
       
   607 							_x( '%s update failed.', 'plugin' ),
       
   608 							response.pluginName
       
   609 						)
       
   610 					);
   505 			} else {
   611 			} else {
   506 				$card.find( '.update-now' ).removeAttr( 'aria-label' );
   612 				$card.find( '.update-now' ).removeAttr( 'aria-label' );
   507 			}
   613 			}
   508 
   614 
   509 			$card.on( 'click', '.notice.is-dismissible .notice-dismiss', function() {
   615 			$card.on( 'click', '.notice.is-dismissible .notice-dismiss', function() {
   514 						.removeClass( 'plugin-card-update-failed' )
   620 						.removeClass( 'plugin-card-update-failed' )
   515 						.find( '.column-name a' ).focus();
   621 						.find( '.column-name a' ).focus();
   516 
   622 
   517 					$card.find( '.update-now' )
   623 					$card.find( '.update-now' )
   518 						.attr( 'aria-label', false )
   624 						.attr( 'aria-label', false )
   519 						.text( wp.updates.l10n.updateNow );
   625 						.text( __( 'Update Now' ) );
   520 				}, 200 );
   626 				}, 200 );
   521 			} );
   627 			} );
   522 		}
   628 		}
   523 
   629 
   524 		wp.a11y.speak( errorMessage, 'assertive' );
   630 		wp.a11y.speak( errorMessage, 'assertive' );
   529 	/**
   635 	/**
   530 	 * Sends an Ajax request to the server to install a plugin.
   636 	 * Sends an Ajax request to the server to install a plugin.
   531 	 *
   637 	 *
   532 	 * @since 4.6.0
   638 	 * @since 4.6.0
   533 	 *
   639 	 *
   534 	 * @param {object}                args         Arguments.
   640 	 * @param {Object}                args         Arguments.
   535 	 * @param {string}                args.slug    Plugin identifier in the WordPress.org Plugin repository.
   641 	 * @param {string}                args.slug    Plugin identifier in the WordPress.org Plugin repository.
   536 	 * @param {installPluginSuccess=} args.success Optional. Success callback. Default: wp.updates.installPluginSuccess
   642 	 * @param {installPluginSuccess=} args.success Optional. Success callback. Default: wp.updates.installPluginSuccess
   537 	 * @param {installPluginError=}   args.error   Optional. Error callback. Default: wp.updates.installPluginError
   643 	 * @param {installPluginError=}   args.error   Optional. Error callback. Default: wp.updates.installPluginError
   538 	 * @return {$.promise} A jQuery promise that represents the request,
   644 	 * @return {$.promise} A jQuery promise that represents the request,
   539 	 *                     decorated with an abort() method.
   645 	 *                     decorated with an abort() method.
   549 
   655 
   550 		if ( 'import' === pagenow ) {
   656 		if ( 'import' === pagenow ) {
   551 			$message = $( '[data-slug="' + args.slug + '"]' );
   657 			$message = $( '[data-slug="' + args.slug + '"]' );
   552 		}
   658 		}
   553 
   659 
   554 		if ( $message.html() !== wp.updates.l10n.installing ) {
   660 		if ( $message.html() !== __( 'Installing...' ) ) {
   555 			$message.data( 'originaltext', $message.html() );
   661 			$message.data( 'originaltext', $message.html() );
   556 		}
   662 		}
   557 
   663 
   558 		$message
   664 		$message
   559 			.addClass( 'updating-message' )
   665 			.addClass( 'updating-message' )
   560 			.attr( 'aria-label', wp.updates.l10n.pluginInstallingLabel.replace( '%s', $message.data( 'name' ) ) )
   666 			.attr(
   561 			.text( wp.updates.l10n.installing );
   667 				'aria-label',
   562 
   668 				sprintf(
   563 		wp.a11y.speak( wp.updates.l10n.installingMsg, 'polite' );
   669 					/* translators: %s: Plugin name and version. */
       
   670 					_x( 'Installing %s...', 'plugin' ),
       
   671 					$message.data( 'name' )
       
   672 				)
       
   673 			)
       
   674 			.text( __( 'Installing...' ) );
       
   675 
       
   676 		wp.a11y.speak( __( 'Installing... please wait.' ) );
   564 
   677 
   565 		// Remove previous error messages, if any.
   678 		// Remove previous error messages, if any.
   566 		$card.removeClass( 'plugin-card-install-failed' ).find( '.notice.notice-error' ).remove();
   679 		$card.removeClass( 'plugin-card-install-failed' ).find( '.notice.notice-error' ).remove();
   567 
   680 
   568 		$document.trigger( 'wp-plugin-installing', args );
   681 		$document.trigger( 'wp-plugin-installing', args );
   573 	/**
   686 	/**
   574 	 * Updates the UI appropriately after a successful plugin install.
   687 	 * Updates the UI appropriately after a successful plugin install.
   575 	 *
   688 	 *
   576 	 * @since 4.6.0
   689 	 * @since 4.6.0
   577 	 *
   690 	 *
   578 	 * @param {object} response             Response from the server.
   691 	 * @param {Object} response             Response from the server.
   579 	 * @param {string} response.slug        Slug of the installed plugin.
   692 	 * @param {string} response.slug        Slug of the installed plugin.
   580 	 * @param {string} response.pluginName  Name of the installed plugin.
   693 	 * @param {string} response.pluginName  Name of the installed plugin.
   581 	 * @param {string} response.activateUrl URL to activate the just installed plugin.
   694 	 * @param {string} response.activateUrl URL to activate the just installed plugin.
   582 	 */
   695 	 */
   583 	wp.updates.installPluginSuccess = function( response ) {
   696 	wp.updates.installPluginSuccess = function( response ) {
   584 		var $message = $( '.plugin-card-' + response.slug ).find( '.install-now' );
   697 		var $message = $( '.plugin-card-' + response.slug ).find( '.install-now' );
   585 
   698 
   586 		$message
   699 		$message
   587 			.removeClass( 'updating-message' )
   700 			.removeClass( 'updating-message' )
   588 			.addClass( 'updated-message installed button-disabled' )
   701 			.addClass( 'updated-message installed button-disabled' )
   589 			.attr( 'aria-label', wp.updates.l10n.pluginInstalledLabel.replace( '%s', response.pluginName ) )
   702 			.attr(
   590 			.text( wp.updates.l10n.pluginInstalled );
   703 				'aria-label',
   591 
   704 				sprintf(
   592 		wp.a11y.speak( wp.updates.l10n.installedMsg, 'polite' );
   705 					/* translators: %s: Plugin name and version. */
       
   706 					_x( '%s installed!', 'plugin' ),
       
   707 					response.pluginName
       
   708 				)
       
   709 			)
       
   710 			.text( _x( 'Installed!', 'plugin' ) );
       
   711 
       
   712 		wp.a11y.speak( __( 'Installation completed successfully.' ) );
   593 
   713 
   594 		$document.trigger( 'wp-plugin-install-success', response );
   714 		$document.trigger( 'wp-plugin-install-success', response );
   595 
   715 
   596 		if ( response.activateUrl ) {
   716 		if ( response.activateUrl ) {
   597 			setTimeout( function() {
   717 			setTimeout( function() {
   598 
   718 
   599 				// Transform the 'Install' button into an 'Activate' button.
   719 				// Transform the 'Install' button into an 'Activate' button.
   600 				$message.removeClass( 'install-now installed button-disabled updated-message' ).addClass( 'activate-now button-primary' )
   720 				$message.removeClass( 'install-now installed button-disabled updated-message' )
   601 					.attr( 'href', response.activateUrl )
   721 					.addClass( 'activate-now button-primary' )
   602 					.attr( 'aria-label', wp.updates.l10n.activatePluginLabel.replace( '%s', response.pluginName ) )
   722 					.attr( 'href', response.activateUrl );
   603 					.text( wp.updates.l10n.activatePlugin );
   723 
       
   724 				if ( 'plugins-network' === pagenow ) {
       
   725 					$message
       
   726 						.attr(
       
   727 							'aria-label',
       
   728 							sprintf(
       
   729 								/* translators: %s: Plugin name. */
       
   730 								_x( 'Network Activate %s', 'plugin' ),
       
   731 								response.pluginName
       
   732 							)
       
   733 						)
       
   734 						.text( __( 'Network Activate' ) );
       
   735 				} else {
       
   736 					$message
       
   737 						.attr(
       
   738 							'aria-label',
       
   739 							sprintf(
       
   740 								/* translators: %s: Plugin name. */
       
   741 								_x( 'Activate %s', 'plugin' ),
       
   742 								response.pluginName
       
   743 							)
       
   744 						)
       
   745 						.text( __( 'Activate' ) );
       
   746 				}
   604 			}, 1000 );
   747 			}, 1000 );
   605 		}
   748 		}
   606 	};
   749 	};
   607 
   750 
   608 	/**
   751 	/**
   609 	 * Updates the UI appropriately after a failed plugin install.
   752 	 * Updates the UI appropriately after a failed plugin install.
   610 	 *
   753 	 *
   611 	 * @since 4.6.0
   754 	 * @since 4.6.0
   612 	 *
   755 	 *
   613 	 * @param {object}  response              Response from the server.
   756 	 * @param {Object}  response              Response from the server.
   614 	 * @param {string}  response.slug         Slug of the plugin to be installed.
   757 	 * @param {string}  response.slug         Slug of the plugin to be installed.
   615 	 * @param {string=} response.pluginName   Optional. Name of the plugin to be installed.
   758 	 * @param {string=} response.pluginName   Optional. Name of the plugin to be installed.
   616 	 * @param {string}  response.errorCode    Error code for the error that occurred.
   759 	 * @param {string}  response.errorCode    Error code for the error that occurred.
   617 	 * @param {string}  response.errorMessage The error that occurred.
   760 	 * @param {string}  response.errorMessage The error that occurred.
   618 	 */
   761 	 */
   627 
   770 
   628 		if ( wp.updates.maybeHandleCredentialError( response, 'install-plugin' ) ) {
   771 		if ( wp.updates.maybeHandleCredentialError( response, 'install-plugin' ) ) {
   629 			return;
   772 			return;
   630 		}
   773 		}
   631 
   774 
   632 		errorMessage = wp.updates.l10n.installFailed.replace( '%s', response.errorMessage );
   775 		errorMessage = sprintf(
       
   776 			/* translators: %s: Error string for a failed installation. */
       
   777 			__( 'Installation failed: %s' ),
       
   778 			response.errorMessage
       
   779 		);
   633 
   780 
   634 		$card
   781 		$card
   635 			.addClass( 'plugin-card-update-failed' )
   782 			.addClass( 'plugin-card-update-failed' )
   636 			.append( '<div class="notice notice-error notice-alt is-dismissible"><p>' + errorMessage + '</p></div>' );
   783 			.append( '<div class="notice notice-error notice-alt is-dismissible"><p>' + errorMessage + '</p></div>' );
   637 
   784 
   645 			}, 200 );
   792 			}, 200 );
   646 		} );
   793 		} );
   647 
   794 
   648 		$button
   795 		$button
   649 			.removeClass( 'updating-message' ).addClass( 'button-disabled' )
   796 			.removeClass( 'updating-message' ).addClass( 'button-disabled' )
   650 			.attr( 'aria-label', wp.updates.l10n.pluginInstallFailedLabel.replace( '%s', $button.data( 'name' ) ) )
   797 			.attr(
   651 			.text( wp.updates.l10n.installFailedShort );
   798 				'aria-label',
       
   799 				sprintf(
       
   800 					/* translators: %s: Plugin name and version. */
       
   801 					_x( '%s installation failed', 'plugin' ),
       
   802 					$button.data( 'name' )
       
   803 				)
       
   804 			)
       
   805 			.text( __( 'Installation failed.' ) );
   652 
   806 
   653 		wp.a11y.speak( errorMessage, 'assertive' );
   807 		wp.a11y.speak( errorMessage, 'assertive' );
   654 
   808 
   655 		$document.trigger( 'wp-plugin-install-error', response );
   809 		$document.trigger( 'wp-plugin-install-error', response );
   656 	};
   810 	};
   658 	/**
   812 	/**
   659 	 * Updates the UI appropriately after a successful importer install.
   813 	 * Updates the UI appropriately after a successful importer install.
   660 	 *
   814 	 *
   661 	 * @since 4.6.0
   815 	 * @since 4.6.0
   662 	 *
   816 	 *
   663 	 * @param {object} response             Response from the server.
   817 	 * @param {Object} response             Response from the server.
   664 	 * @param {string} response.slug        Slug of the installed plugin.
   818 	 * @param {string} response.slug        Slug of the installed plugin.
   665 	 * @param {string} response.pluginName  Name of the installed plugin.
   819 	 * @param {string} response.pluginName  Name of the installed plugin.
   666 	 * @param {string} response.activateUrl URL to activate the just installed plugin.
   820 	 * @param {string} response.activateUrl URL to activate the just installed plugin.
   667 	 */
   821 	 */
   668 	wp.updates.installImporterSuccess = function( response ) {
   822 	wp.updates.installImporterSuccess = function( response ) {
   669 		wp.updates.addAdminNotice( {
   823 		wp.updates.addAdminNotice( {
   670 			id:        'install-success',
   824 			id:        'install-success',
   671 			className: 'notice-success is-dismissible',
   825 			className: 'notice-success is-dismissible',
   672 			message:   wp.updates.l10n.importerInstalledMsg.replace( '%s', response.activateUrl + '&from=import' )
   826 			message:   sprintf(
       
   827 				/* translators: %s: Activation URL. */
       
   828 				__( 'Importer installed successfully. <a href="%s">Run importer</a>' ),
       
   829 				response.activateUrl + '&from=import'
       
   830 			)
   673 		} );
   831 		} );
   674 
   832 
   675 		$( '[data-slug="' + response.slug + '"]' )
   833 		$( '[data-slug="' + response.slug + '"]' )
   676 			.removeClass( 'install-now updating-message' )
   834 			.removeClass( 'install-now updating-message' )
   677 			.addClass( 'activate-now' )
   835 			.addClass( 'activate-now' )
   678 			.attr({
   836 			.attr({
   679 				'href': response.activateUrl + '&from=import',
   837 				'href': response.activateUrl + '&from=import',
   680 				'aria-label': wp.updates.l10n.activateImporterLabel.replace( '%s', response.pluginName )
   838 				'aria-label':sprintf(
       
   839 					/* translators: %s: Importer name. */
       
   840 					__( 'Run %s' ),
       
   841 					response.pluginName
       
   842 				)
   681 			})
   843 			})
   682 			.text( wp.updates.l10n.activateImporter );
   844 			.text( __( 'Run Importer' ) );
   683 
   845 
   684 		wp.a11y.speak( wp.updates.l10n.installedMsg, 'polite' );
   846 		wp.a11y.speak( __( 'Installation completed successfully.' ) );
   685 
   847 
   686 		$document.trigger( 'wp-importer-install-success', response );
   848 		$document.trigger( 'wp-importer-install-success', response );
   687 	};
   849 	};
   688 
   850 
   689 	/**
   851 	/**
   690 	 * Updates the UI appropriately after a failed importer install.
   852 	 * Updates the UI appropriately after a failed importer install.
   691 	 *
   853 	 *
   692 	 * @since 4.6.0
   854 	 * @since 4.6.0
   693 	 *
   855 	 *
   694 	 * @param {object}  response              Response from the server.
   856 	 * @param {Object}  response              Response from the server.
   695 	 * @param {string}  response.slug         Slug of the plugin to be installed.
   857 	 * @param {string}  response.slug         Slug of the plugin to be installed.
   696 	 * @param {string=} response.pluginName   Optional. Name of the plugin to be installed.
   858 	 * @param {string=} response.pluginName   Optional. Name of the plugin to be installed.
   697 	 * @param {string}  response.errorCode    Error code for the error that occurred.
   859 	 * @param {string}  response.errorCode    Error code for the error that occurred.
   698 	 * @param {string}  response.errorMessage The error that occurred.
   860 	 * @param {string}  response.errorMessage The error that occurred.
   699 	 */
   861 	 */
   700 	wp.updates.installImporterError = function( response ) {
   862 	wp.updates.installImporterError = function( response ) {
   701 		var errorMessage = wp.updates.l10n.installFailed.replace( '%s', response.errorMessage ),
   863 		var errorMessage = sprintf(
       
   864 				/* translators: %s: Error string for a failed installation. */
       
   865 				__( 'Installation failed: %s' ),
       
   866 				response.errorMessage
       
   867 			),
   702 			$installLink = $( '[data-slug="' + response.slug + '"]' ),
   868 			$installLink = $( '[data-slug="' + response.slug + '"]' ),
   703 			pluginName = $installLink.data( 'name' );
   869 			pluginName = $installLink.data( 'name' );
   704 
   870 
   705 		if ( ! wp.updates.isValidResponse( response, 'install' ) ) {
   871 		if ( ! wp.updates.isValidResponse( response, 'install' ) ) {
   706 			return;
   872 			return;
   716 			message:   errorMessage
   882 			message:   errorMessage
   717 		} );
   883 		} );
   718 
   884 
   719 		$installLink
   885 		$installLink
   720 			.removeClass( 'updating-message' )
   886 			.removeClass( 'updating-message' )
   721 			.text( wp.updates.l10n.installNow )
   887 			.attr(
   722 			.attr( 'aria-label', wp.updates.l10n.pluginInstallNowLabel.replace( '%s', pluginName ) );
   888 				'aria-label',
       
   889 				sprintf(
       
   890 					/* translators: %s: Plugin name. */
       
   891 					_x( 'Install %s now', 'plugin' ),
       
   892 					pluginName
       
   893 				)
       
   894 			)
       
   895 			.text( __( 'Install Now' ) );
   723 
   896 
   724 		wp.a11y.speak( errorMessage, 'assertive' );
   897 		wp.a11y.speak( errorMessage, 'assertive' );
   725 
   898 
   726 		$document.trigger( 'wp-importer-install-error', response );
   899 		$document.trigger( 'wp-importer-install-error', response );
   727 	};
   900 	};
   729 	/**
   902 	/**
   730 	 * Sends an Ajax request to the server to delete a plugin.
   903 	 * Sends an Ajax request to the server to delete a plugin.
   731 	 *
   904 	 *
   732 	 * @since 4.6.0
   905 	 * @since 4.6.0
   733 	 *
   906 	 *
   734 	 * @param {object}               args         Arguments.
   907 	 * @param {Object}               args         Arguments.
   735 	 * @param {string}               args.plugin  Basename of the plugin to be deleted.
   908 	 * @param {string}               args.plugin  Basename of the plugin to be deleted.
   736 	 * @param {string}               args.slug    Slug of the plugin to be deleted.
   909 	 * @param {string}               args.slug    Slug of the plugin to be deleted.
   737 	 * @param {deletePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.deletePluginSuccess
   910 	 * @param {deletePluginSuccess=} args.success Optional. Success callback. Default: wp.updates.deletePluginSuccess
   738 	 * @param {deletePluginError=}   args.error   Optional. Error callback. Default: wp.updates.deletePluginError
   911 	 * @param {deletePluginError=}   args.error   Optional. Error callback. Default: wp.updates.deletePluginError
   739 	 * @return {$.promise} A jQuery promise that represents the request,
   912 	 * @return {$.promise} A jQuery promise that represents the request,
   745 		args = _.extend( {
   918 		args = _.extend( {
   746 			success: wp.updates.deletePluginSuccess,
   919 			success: wp.updates.deletePluginSuccess,
   747 			error: wp.updates.deletePluginError
   920 			error: wp.updates.deletePluginError
   748 		}, args );
   921 		}, args );
   749 
   922 
   750 		if ( $link.html() !== wp.updates.l10n.deleting ) {
   923 		if ( $link.html() !== __( 'Deleting...' ) ) {
   751 			$link
   924 			$link
   752 				.data( 'originaltext', $link.html() )
   925 				.data( 'originaltext', $link.html() )
   753 				.text( wp.updates.l10n.deleting );
   926 				.text( __( 'Deleting...' ) );
   754 		}
   927 		}
   755 
   928 
   756 		wp.a11y.speak( wp.updates.l10n.deleting, 'polite' );
   929 		wp.a11y.speak( __( 'Deleting...' ) );
   757 
   930 
   758 		$document.trigger( 'wp-plugin-deleting', args );
   931 		$document.trigger( 'wp-plugin-deleting', args );
   759 
   932 
   760 		return wp.updates.ajax( 'delete-plugin', args );
   933 		return wp.updates.ajax( 'delete-plugin', args );
   761 	};
   934 	};
   841 			} else {
  1014 			} else {
   842 				$form.find( '.tablenav' ).css( { visibility: 'hidden' } );
  1015 				$form.find( '.tablenav' ).css( { visibility: 'hidden' } );
   843 				$views.find( '.all' ).remove();
  1016 				$views.find( '.all' ).remove();
   844 
  1017 
   845 				if ( ! $form.find( 'tr.no-items' ).length ) {
  1018 				if ( ! $form.find( 'tr.no-items' ).length ) {
   846 					$form.find( '#the-list' ).append( '<tr class="no-items"><td class="colspanchange" colspan="' + columnCount + '">' + wp.updates.l10n.noPlugins + '</td></tr>' );
  1019 					$form.find( '#the-list' ).append( '<tr class="no-items"><td class="colspanchange" colspan="' + columnCount + '">' + __( 'No plugins are currently available.' ) + '</td></tr>' );
   847 				}
  1020 				}
   848 			}
  1021 			}
   849 		} );
  1022 		} );
   850 
  1023 
   851 		wp.a11y.speak( wp.updates.l10n.pluginDeleted, 'polite' );
  1024 		wp.a11y.speak( _x( 'Deleted!', 'plugin' ) );
   852 
  1025 
   853 		$document.trigger( 'wp-plugin-delete-success', response );
  1026 		$document.trigger( 'wp-plugin-delete-success', response );
   854 	};
  1027 	};
   855 
  1028 
   856 	/**
  1029 	/**
   857 	 * Updates the UI appropriately after a failed plugin deletion.
  1030 	 * Updates the UI appropriately after a failed plugin deletion.
   858 	 *
  1031 	 *
   859 	 * @since 4.6.0
  1032 	 * @since 4.6.0
   860 	 *
  1033 	 *
   861 	 * @param {object}  response              Response from the server.
  1034 	 * @param {Object}  response              Response from the server.
   862 	 * @param {string}  response.slug         Slug of the plugin to be deleted.
  1035 	 * @param {string}  response.slug         Slug of the plugin to be deleted.
   863 	 * @param {string}  response.plugin       Base name of the plugin to be deleted
  1036 	 * @param {string}  response.plugin       Base name of the plugin to be deleted
   864 	 * @param {string=} response.pluginName   Optional. Name of the plugin to be deleted.
  1037 	 * @param {string=} response.pluginName   Optional. Name of the plugin to be deleted.
   865 	 * @param {string}  response.errorCode    Error code for the error that occurred.
  1038 	 * @param {string}  response.errorCode    Error code for the error that occurred.
   866 	 * @param {string}  response.errorMessage The error that occurred.
  1039 	 * @param {string}  response.errorMessage The error that occurred.
   913 	/**
  1086 	/**
   914 	 * Sends an Ajax request to the server to update a theme.
  1087 	 * Sends an Ajax request to the server to update a theme.
   915 	 *
  1088 	 *
   916 	 * @since 4.6.0
  1089 	 * @since 4.6.0
   917 	 *
  1090 	 *
   918 	 * @param {object}              args         Arguments.
  1091 	 * @param {Object}              args         Arguments.
   919 	 * @param {string}              args.slug    Theme stylesheet.
  1092 	 * @param {string}              args.slug    Theme stylesheet.
   920 	 * @param {updateThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.updateThemeSuccess
  1093 	 * @param {updateThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.updateThemeSuccess
   921 	 * @param {updateThemeError=}   args.error   Optional. Error callback. Default: wp.updates.updateThemeError
  1094 	 * @param {updateThemeError=}   args.error   Optional. Error callback. Default: wp.updates.updateThemeError
   922 	 * @return {$.promise} A jQuery promise that represents the request,
  1095 	 * @return {$.promise} A jQuery promise that represents the request,
   923 	 *                     decorated with an abort() method.
  1096 	 *                     decorated with an abort() method.
   951 
  1124 
   952 			$notice = $notice.add( $( '[data-slug="' + args.slug + '"]' ).find( '.update-message' ) );
  1125 			$notice = $notice.add( $( '[data-slug="' + args.slug + '"]' ).find( '.update-message' ) );
   953 			$notice = $notice.addClass( 'updating-message' ).find( 'p' );
  1126 			$notice = $notice.addClass( 'updating-message' ).find( 'p' );
   954 		}
  1127 		}
   955 
  1128 
   956 		if ( $notice.html() !== wp.updates.l10n.updating ) {
  1129 		if ( $notice.html() !== __( 'Updating...' ) ) {
   957 			$notice.data( 'originaltext', $notice.html() );
  1130 			$notice.data( 'originaltext', $notice.html() );
   958 		}
  1131 		}
   959 
  1132 
   960 		wp.a11y.speak( wp.updates.l10n.updatingMsg, 'polite' );
  1133 		wp.a11y.speak( __( 'Updating... please wait.' ) );
   961 		$notice.text( wp.updates.l10n.updating );
  1134 		$notice.text( __( 'Updating...' ) );
   962 
  1135 
   963 		$document.trigger( 'wp-theme-updating', args );
  1136 		$document.trigger( 'wp-theme-updating', args );
   964 
  1137 
   965 		return wp.updates.ajax( 'update-theme', args );
  1138 		return wp.updates.ajax( 'update-theme', args );
   966 	};
  1139 	};
   967 
  1140 
   968 	/**
  1141 	/**
   969 	 * Updates the UI appropriately after a successful theme update.
  1142 	 * Updates the UI appropriately after a successful theme update.
   970 	 *
  1143 	 *
   971 	 * @since 4.6.0
  1144 	 * @since 4.6.0
   972 	 *
  1145 	 * @since 5.5.0 Auto-update "time to next update" text cleared.
   973 	 * @param {object} response
  1146 	 *
       
  1147 	 * @param {Object} response
   974 	 * @param {string} response.slug       Slug of the theme to be updated.
  1148 	 * @param {string} response.slug       Slug of the theme to be updated.
   975 	 * @param {object} response.theme      Updated theme.
  1149 	 * @param {Object} response.theme      Updated theme.
   976 	 * @param {string} response.oldVersion Old version of the theme.
  1150 	 * @param {string} response.oldVersion Old version of the theme.
   977 	 * @param {string} response.newVersion New version of the theme.
  1151 	 * @param {string} response.newVersion New version of the theme.
   978 	 */
  1152 	 */
   979 	wp.updates.updateThemeSuccess = function( response ) {
  1153 	wp.updates.updateThemeSuccess = function( response ) {
   980 		var isModalOpen    = $( 'body.modal-open' ).length,
  1154 		var isModalOpen    = $( 'body.modal-open' ).length,
   981 			$theme         = $( '[data-slug="' + response.slug + '"]' ),
  1155 			$theme         = $( '[data-slug="' + response.slug + '"]' ),
   982 			updatedMessage = {
  1156 			updatedMessage = {
   983 				className: 'updated-message notice-success notice-alt',
  1157 				className: 'updated-message notice-success notice-alt',
   984 				message:   wp.updates.l10n.themeUpdated
  1158 				message:   _x( 'Updated!', 'theme' )
   985 			},
  1159 			},
   986 			$notice, newText;
  1160 			$notice, newText;
   987 
  1161 
   988 		if ( 'customize' === pagenow ) {
  1162 		if ( 'customize' === pagenow ) {
   989 			$theme = $( '.updating-message' ).siblings( '.theme-name' );
  1163 			$theme = $( '.updating-message' ).siblings( '.theme-name' );
  1000 			$notice = $theme.find( '.update-message' );
  1174 			$notice = $theme.find( '.update-message' );
  1001 
  1175 
  1002 			// Update the version number in the row.
  1176 			// Update the version number in the row.
  1003 			newText = $theme.find( '.theme-version-author-uri' ).html().replace( response.oldVersion, response.newVersion );
  1177 			newText = $theme.find( '.theme-version-author-uri' ).html().replace( response.oldVersion, response.newVersion );
  1004 			$theme.find( '.theme-version-author-uri' ).html( newText );
  1178 			$theme.find( '.theme-version-author-uri' ).html( newText );
       
  1179 
       
  1180 			// Clear the "time to next auto-update" text.
       
  1181 			$theme.find( '.auto-update-time' ).empty();
  1005 		} else {
  1182 		} else {
  1006 			$notice = $( '.theme-info .notice' ).add( $theme.find( '.update-message' ) );
  1183 			$notice = $( '.theme-info .notice' ).add( $theme.find( '.update-message' ) );
  1007 
  1184 
  1008 			// Focus on Customize button after updating.
  1185 			// Focus on Customize button after updating.
  1009 			if ( isModalOpen ) {
  1186 			if ( isModalOpen ) {
  1010 				$( '.load-customize:visible' ).focus();
  1187 				$( '.load-customize:visible' ).focus();
       
  1188 				$( '.theme-info .theme-autoupdate' ).find( '.auto-update-time' ).empty();
  1011 			} else {
  1189 			} else {
  1012 				$theme.find( '.load-customize' ).focus();
  1190 				$theme.find( '.load-customize' ).focus();
  1013 			}
  1191 			}
  1014 		}
  1192 		}
  1015 
  1193 
  1016 		wp.updates.addAdminNotice( _.extend( { selector: $notice }, updatedMessage ) );
  1194 		wp.updates.addAdminNotice( _.extend( { selector: $notice }, updatedMessage ) );
  1017 		wp.a11y.speak( wp.updates.l10n.updatedMsg, 'polite' );
  1195 		wp.a11y.speak( __( 'Update completed successfully.' ) );
  1018 
  1196 
  1019 		wp.updates.decrementCount( 'theme' );
  1197 		wp.updates.decrementCount( 'theme' );
  1020 
  1198 
  1021 		$document.trigger( 'wp-theme-update-success', response );
  1199 		$document.trigger( 'wp-theme-update-success', response );
  1022 
  1200 
  1029 	/**
  1207 	/**
  1030 	 * Updates the UI appropriately after a failed theme update.
  1208 	 * Updates the UI appropriately after a failed theme update.
  1031 	 *
  1209 	 *
  1032 	 * @since 4.6.0
  1210 	 * @since 4.6.0
  1033 	 *
  1211 	 *
  1034 	 * @param {object} response              Response from the server.
  1212 	 * @param {Object} response              Response from the server.
  1035 	 * @param {string} response.slug         Slug of the theme to be updated.
  1213 	 * @param {string} response.slug         Slug of the theme to be updated.
  1036 	 * @param {string} response.errorCode    Error code for the error that occurred.
  1214 	 * @param {string} response.errorCode    Error code for the error that occurred.
  1037 	 * @param {string} response.errorMessage The error that occurred.
  1215 	 * @param {string} response.errorMessage The error that occurred.
  1038 	 */
  1216 	 */
  1039 	wp.updates.updateThemeError = function( response ) {
  1217 	wp.updates.updateThemeError = function( response ) {
  1040 		var $theme       = $( '[data-slug="' + response.slug + '"]' ),
  1218 		var $theme       = $( '[data-slug="' + response.slug + '"]' ),
  1041 			errorMessage = wp.updates.l10n.updateFailed.replace( '%s', response.errorMessage ),
  1219 			errorMessage = sprintf(
       
  1220 				/* translators: %s: Error string for a failed update. */
       
  1221 				 __( 'Update failed: %s' ),
       
  1222 				response.errorMessage
       
  1223 			),
  1042 			$notice;
  1224 			$notice;
  1043 
  1225 
  1044 		if ( ! wp.updates.isValidResponse( response, 'update' ) ) {
  1226 		if ( ! wp.updates.isValidResponse( response, 'update' ) ) {
  1045 			return;
  1227 			return;
  1046 		}
  1228 		}
  1065 			selector:  $notice,
  1247 			selector:  $notice,
  1066 			className: 'update-message notice-error notice-alt is-dismissible',
  1248 			className: 'update-message notice-error notice-alt is-dismissible',
  1067 			message:   errorMessage
  1249 			message:   errorMessage
  1068 		} );
  1250 		} );
  1069 
  1251 
  1070 		wp.a11y.speak( errorMessage, 'polite' );
  1252 		wp.a11y.speak( errorMessage );
  1071 
  1253 
  1072 		$document.trigger( 'wp-theme-update-error', response );
  1254 		$document.trigger( 'wp-theme-update-error', response );
  1073 	};
  1255 	};
  1074 
  1256 
  1075 	/**
  1257 	/**
  1076 	 * Sends an Ajax request to the server to install a theme.
  1258 	 * Sends an Ajax request to the server to install a theme.
  1077 	 *
  1259 	 *
  1078 	 * @since 4.6.0
  1260 	 * @since 4.6.0
  1079 	 *
  1261 	 *
  1080 	 * @param {object}               args
  1262 	 * @param {Object}               args
  1081 	 * @param {string}               args.slug    Theme stylesheet.
  1263 	 * @param {string}               args.slug    Theme stylesheet.
  1082 	 * @param {installThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.installThemeSuccess
  1264 	 * @param {installThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.installThemeSuccess
  1083 	 * @param {installThemeError=}   args.error   Optional. Error callback. Default: wp.updates.installThemeError
  1265 	 * @param {installThemeError=}   args.error   Optional. Error callback. Default: wp.updates.installThemeError
  1084 	 * @return {$.promise} A jQuery promise that represents the request,
  1266 	 * @return {$.promise} A jQuery promise that represents the request,
  1085 	 *                     decorated with an abort() method.
  1267 	 *                     decorated with an abort() method.
  1092 			error: wp.updates.installThemeError
  1274 			error: wp.updates.installThemeError
  1093 		}, args );
  1275 		}, args );
  1094 
  1276 
  1095 		$message.addClass( 'updating-message' );
  1277 		$message.addClass( 'updating-message' );
  1096 		$message.parents( '.theme' ).addClass( 'focus' );
  1278 		$message.parents( '.theme' ).addClass( 'focus' );
  1097 		if ( $message.html() !== wp.updates.l10n.installing ) {
  1279 		if ( $message.html() !== __( 'Installing...' ) ) {
  1098 			$message.data( 'originaltext', $message.html() );
  1280 			$message.data( 'originaltext', $message.html() );
  1099 		}
  1281 		}
  1100 
  1282 
  1101 		$message
  1283 		$message
  1102 			.text( wp.updates.l10n.installing )
  1284 			.attr(
  1103 			.attr( 'aria-label', wp.updates.l10n.themeInstallingLabel.replace( '%s', $message.data( 'name' ) ) );
  1285 				'aria-label',
  1104 		wp.a11y.speak( wp.updates.l10n.installingMsg, 'polite' );
  1286 				sprintf(
       
  1287 					/* translators: %s: Theme name and version. */
       
  1288 					_x( 'Installing %s...', 'theme' ),
       
  1289 					$message.data( 'name' )
       
  1290 				)
       
  1291 			)
       
  1292 			.text( __( 'Installing...' ) );
       
  1293 
       
  1294 		wp.a11y.speak( __( 'Installing... please wait.' ) );
  1105 
  1295 
  1106 		// Remove previous error messages, if any.
  1296 		// Remove previous error messages, if any.
  1107 		$( '.install-theme-info, [data-slug="' + args.slug + '"]' ).removeClass( 'theme-install-failed' ).find( '.notice.notice-error' ).remove();
  1297 		$( '.install-theme-info, [data-slug="' + args.slug + '"]' ).removeClass( 'theme-install-failed' ).find( '.notice.notice-error' ).remove();
  1108 
  1298 
  1109 		$document.trigger( 'wp-theme-installing', args );
  1299 		$document.trigger( 'wp-theme-installing', args );
  1114 	/**
  1304 	/**
  1115 	 * Updates the UI appropriately after a successful theme install.
  1305 	 * Updates the UI appropriately after a successful theme install.
  1116 	 *
  1306 	 *
  1117 	 * @since 4.6.0
  1307 	 * @since 4.6.0
  1118 	 *
  1308 	 *
  1119 	 * @param {object} response              Response from the server.
  1309 	 * @param {Object} response              Response from the server.
  1120 	 * @param {string} response.slug         Slug of the theme to be installed.
  1310 	 * @param {string} response.slug         Slug of the theme to be installed.
  1121 	 * @param {string} response.customizeUrl URL to the Customizer for the just installed theme.
  1311 	 * @param {string} response.customizeUrl URL to the Customizer for the just installed theme.
  1122 	 * @param {string} response.activateUrl  URL to activate the just installed theme.
  1312 	 * @param {string} response.activateUrl  URL to activate the just installed theme.
  1123 	 */
  1313 	 */
  1124 	wp.updates.installThemeSuccess = function( response ) {
  1314 	wp.updates.installThemeSuccess = function( response ) {
  1128 		$document.trigger( 'wp-theme-install-success', response );
  1318 		$document.trigger( 'wp-theme-install-success', response );
  1129 
  1319 
  1130 		$message = $card.find( '.button-primary' )
  1320 		$message = $card.find( '.button-primary' )
  1131 			.removeClass( 'updating-message' )
  1321 			.removeClass( 'updating-message' )
  1132 			.addClass( 'updated-message disabled' )
  1322 			.addClass( 'updated-message disabled' )
  1133 			.attr( 'aria-label', wp.updates.l10n.themeInstalledLabel.replace( '%s', response.themeName ) )
  1323 			.attr(
  1134 			.text( wp.updates.l10n.themeInstalled );
  1324 				'aria-label',
  1135 
  1325 				sprintf(
  1136 		wp.a11y.speak( wp.updates.l10n.installedMsg, 'polite' );
  1326 					/* translators: %s: Theme name and version. */
       
  1327 					_x( '%s installed!', 'theme' ),
       
  1328 					response.themeName
       
  1329 				)
       
  1330 			)
       
  1331 			.text( _x( 'Installed!', 'theme' ) );
       
  1332 
       
  1333 		wp.a11y.speak( __( 'Installation completed successfully.' ) );
  1137 
  1334 
  1138 		setTimeout( function() {
  1335 		setTimeout( function() {
  1139 
  1336 
  1140 			if ( response.activateUrl ) {
  1337 			if ( response.activateUrl ) {
  1141 
  1338 
  1142 				// Transform the 'Install' button into an 'Activate' button.
  1339 				// Transform the 'Install' button into an 'Activate' button.
  1143 				$message
  1340 				$message
  1144 					.attr( 'href', response.activateUrl )
  1341 					.attr( 'href', response.activateUrl )
  1145 					.removeClass( 'theme-install updated-message disabled' )
  1342 					.removeClass( 'theme-install updated-message disabled' )
  1146 					.addClass( 'activate' )
  1343 					.addClass( 'activate' );
  1147 					.attr( 'aria-label', wp.updates.l10n.activateThemeLabel.replace( '%s', response.themeName ) )
  1344 
  1148 					.text( wp.updates.l10n.activateTheme );
  1345 				if ( 'themes-network' === pagenow ) {
       
  1346 					$message
       
  1347 						.attr(
       
  1348 							'aria-label',
       
  1349 							sprintf(
       
  1350 								/* translators: %s: Theme name. */
       
  1351 								_x( 'Network Activate %s', 'theme' ),
       
  1352 								response.themeName
       
  1353 							)
       
  1354 						)
       
  1355 						.text( __( 'Network Enable' ) );
       
  1356 				} else {
       
  1357 					$message
       
  1358 						.attr(
       
  1359 							'aria-label',
       
  1360 							sprintf(
       
  1361 								/* translators: %s: Theme name. */
       
  1362 								_x( 'Activate %s', 'theme' ),
       
  1363 								response.themeName
       
  1364 							)
       
  1365 						)
       
  1366 						.text( __( 'Activate' ) );
       
  1367 				}
  1149 			}
  1368 			}
  1150 
  1369 
  1151 			if ( response.customizeUrl ) {
  1370 			if ( response.customizeUrl ) {
  1152 
  1371 
  1153 				// Transform the 'Preview' button into a 'Live Preview' button.
  1372 				// Transform the 'Preview' button into a 'Live Preview' button.
  1154 				$message.siblings( '.preview' ).replaceWith( function () {
  1373 				$message.siblings( '.preview' ).replaceWith( function () {
  1155 					return $( '<a>' )
  1374 					return $( '<a>' )
  1156 						.attr( 'href', response.customizeUrl )
  1375 						.attr( 'href', response.customizeUrl )
  1157 						.addClass( 'button load-customize' )
  1376 						.addClass( 'button load-customize' )
  1158 						.text( wp.updates.l10n.livePreview );
  1377 						.text( __( 'Live Preview' ) );
  1159 				} );
  1378 				} );
  1160 			}
  1379 			}
  1161 		}, 1000 );
  1380 		}, 1000 );
  1162 	};
  1381 	};
  1163 
  1382 
  1164 	/**
  1383 	/**
  1165 	 * Updates the UI appropriately after a failed theme install.
  1384 	 * Updates the UI appropriately after a failed theme install.
  1166 	 *
  1385 	 *
  1167 	 * @since 4.6.0
  1386 	 * @since 4.6.0
  1168 	 *
  1387 	 *
  1169 	 * @param {object} response              Response from the server.
  1388 	 * @param {Object} response              Response from the server.
  1170 	 * @param {string} response.slug         Slug of the theme to be installed.
  1389 	 * @param {string} response.slug         Slug of the theme to be installed.
  1171 	 * @param {string} response.errorCode    Error code for the error that occurred.
  1390 	 * @param {string} response.errorCode    Error code for the error that occurred.
  1172 	 * @param {string} response.errorMessage The error that occurred.
  1391 	 * @param {string} response.errorMessage The error that occurred.
  1173 	 */
  1392 	 */
  1174 	wp.updates.installThemeError = function( response ) {
  1393 	wp.updates.installThemeError = function( response ) {
  1175 		var $card, $button,
  1394 		var $card, $button,
  1176 			errorMessage = wp.updates.l10n.installFailed.replace( '%s', response.errorMessage ),
  1395 			errorMessage = sprintf(
       
  1396 				/* translators: %s: Error string for a failed installation. */
       
  1397 				__( 'Installation failed: %s' ),
       
  1398 				response.errorMessage
       
  1399 			),
  1177 			$message     = wp.updates.adminNotice( {
  1400 			$message     = wp.updates.adminNotice( {
  1178 				className: 'update-message notice-error notice-alt',
  1401 				className: 'update-message notice-error notice-alt',
  1179 				message:   errorMessage
  1402 				message:   errorMessage
  1180 			} );
  1403 			} );
  1181 
  1404 
  1206 			}
  1429 			}
  1207 		}
  1430 		}
  1208 
  1431 
  1209 		$button
  1432 		$button
  1210 			.removeClass( 'updating-message' )
  1433 			.removeClass( 'updating-message' )
  1211 			.attr( 'aria-label', wp.updates.l10n.themeInstallFailedLabel.replace( '%s', $button.data( 'name' ) ) )
  1434 			.attr(
  1212 			.text( wp.updates.l10n.installFailedShort );
  1435 				'aria-label',
       
  1436 				sprintf(
       
  1437 					/* translators: %s: Theme name and version. */
       
  1438 					_x( '%s installation failed', 'theme' ),
       
  1439 					$button.data( 'name' )
       
  1440 				)
       
  1441 			)
       
  1442 			.text( __( 'Installation failed.' ) );
  1213 
  1443 
  1214 		wp.a11y.speak( errorMessage, 'assertive' );
  1444 		wp.a11y.speak( errorMessage, 'assertive' );
  1215 
  1445 
  1216 		$document.trigger( 'wp-theme-install-error', response );
  1446 		$document.trigger( 'wp-theme-install-error', response );
  1217 	};
  1447 	};
  1219 	/**
  1449 	/**
  1220 	 * Sends an Ajax request to the server to delete a theme.
  1450 	 * Sends an Ajax request to the server to delete a theme.
  1221 	 *
  1451 	 *
  1222 	 * @since 4.6.0
  1452 	 * @since 4.6.0
  1223 	 *
  1453 	 *
  1224 	 * @param {object}              args
  1454 	 * @param {Object}              args
  1225 	 * @param {string}              args.slug    Theme stylesheet.
  1455 	 * @param {string}              args.slug    Theme stylesheet.
  1226 	 * @param {deleteThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.deleteThemeSuccess
  1456 	 * @param {deleteThemeSuccess=} args.success Optional. Success callback. Default: wp.updates.deleteThemeSuccess
  1227 	 * @param {deleteThemeError=}   args.error   Optional. Error callback. Default: wp.updates.deleteThemeError
  1457 	 * @param {deleteThemeError=}   args.error   Optional. Error callback. Default: wp.updates.deleteThemeError
  1228 	 * @return {$.promise} A jQuery promise that represents the request,
  1458 	 * @return {$.promise} A jQuery promise that represents the request,
  1229 	 *                     decorated with an abort() method.
  1459 	 *                     decorated with an abort() method.
  1240 		args = _.extend( {
  1470 		args = _.extend( {
  1241 			success: wp.updates.deleteThemeSuccess,
  1471 			success: wp.updates.deleteThemeSuccess,
  1242 			error: wp.updates.deleteThemeError
  1472 			error: wp.updates.deleteThemeError
  1243 		}, args );
  1473 		}, args );
  1244 
  1474 
  1245 		if ( $button && $button.html() !== wp.updates.l10n.deleting ) {
  1475 		if ( $button && $button.html() !== __( 'Deleting...' ) ) {
  1246 			$button
  1476 			$button
  1247 				.data( 'originaltext', $button.html() )
  1477 				.data( 'originaltext', $button.html() )
  1248 				.text( wp.updates.l10n.deleting );
  1478 				.text( __( 'Deleting...' ) );
  1249 		}
  1479 		}
  1250 
  1480 
  1251 		wp.a11y.speak( wp.updates.l10n.deleting, 'polite' );
  1481 		wp.a11y.speak( __( 'Deleting...' ) );
  1252 
  1482 
  1253 		// Remove previous error messages, if any.
  1483 		// Remove previous error messages, if any.
  1254 		$( '.theme-info .update-message' ).remove();
  1484 		$( '.theme-info .update-message' ).remove();
  1255 
  1485 
  1256 		$document.trigger( 'wp-theme-deleting', args );
  1486 		$document.trigger( 'wp-theme-deleting', args );
  1261 	/**
  1491 	/**
  1262 	 * Updates the UI appropriately after a successful theme deletion.
  1492 	 * Updates the UI appropriately after a successful theme deletion.
  1263 	 *
  1493 	 *
  1264 	 * @since 4.6.0
  1494 	 * @since 4.6.0
  1265 	 *
  1495 	 *
  1266 	 * @param {object} response      Response from the server.
  1496 	 * @param {Object} response      Response from the server.
  1267 	 * @param {string} response.slug Slug of the theme that was deleted.
  1497 	 * @param {string} response.slug Slug of the theme that was deleted.
  1268 	 */
  1498 	 */
  1269 	wp.updates.deleteThemeSuccess = function( response ) {
  1499 	wp.updates.deleteThemeSuccess = function( response ) {
  1270 		var $themeRows = $( '[data-slug="' + response.slug + '"]' );
  1500 		var $themeRows = $( '[data-slug="' + response.slug + '"]' );
  1271 
  1501 
  1309 				// There is always at least one theme available.
  1539 				// There is always at least one theme available.
  1310 				$views.find( '.all .count' ).text( '(' + --totals.all + ')' );
  1540 				$views.find( '.all .count' ).text( '(' + --totals.all + ')' );
  1311 			} );
  1541 			} );
  1312 		}
  1542 		}
  1313 
  1543 
  1314 		wp.a11y.speak( wp.updates.l10n.themeDeleted, 'polite' );
  1544 		wp.a11y.speak( _x( 'Deleted!', 'theme' ) );
  1315 
  1545 
  1316 		$document.trigger( 'wp-theme-delete-success', response );
  1546 		$document.trigger( 'wp-theme-delete-success', response );
  1317 	};
  1547 	};
  1318 
  1548 
  1319 	/**
  1549 	/**
  1320 	 * Updates the UI appropriately after a failed theme deletion.
  1550 	 * Updates the UI appropriately after a failed theme deletion.
  1321 	 *
  1551 	 *
  1322 	 * @since 4.6.0
  1552 	 * @since 4.6.0
  1323 	 *
  1553 	 *
  1324 	 * @param {object} response              Response from the server.
  1554 	 * @param {Object} response              Response from the server.
  1325 	 * @param {string} response.slug         Slug of the theme to be deleted.
  1555 	 * @param {string} response.slug         Slug of the theme to be deleted.
  1326 	 * @param {string} response.errorCode    Error code for the error that occurred.
  1556 	 * @param {string} response.errorCode    Error code for the error that occurred.
  1327 	 * @param {string} response.errorMessage The error that occurred.
  1557 	 * @param {string} response.errorMessage The error that occurred.
  1328 	 */
  1558 	 */
  1329 	wp.updates.deleteThemeError = function( response ) {
  1559 	wp.updates.deleteThemeError = function( response ) {
  1330 		var $themeRow    = $( 'tr.inactive[data-slug="' + response.slug + '"]' ),
  1560 		var $themeRow    = $( 'tr.inactive[data-slug="' + response.slug + '"]' ),
  1331 			$button      = $( '.theme-actions .delete-theme' ),
  1561 			$button      = $( '.theme-actions .delete-theme' ),
  1332 			updateRow    = wp.template( 'item-update-row' ),
  1562 			updateRow    = wp.template( 'item-update-row' ),
  1333 			$updateRow   = $themeRow.siblings( '#' + response.slug + '-update' ),
  1563 			$updateRow   = $themeRow.siblings( '#' + response.slug + '-update' ),
  1334 			errorMessage = wp.updates.l10n.deleteFailed.replace( '%s', response.errorMessage ),
  1564 			errorMessage = sprintf(
       
  1565 				/* translators: %s: Error string for a failed deletion. */
       
  1566 				__( 'Deletion failed: %s' ),
       
  1567 				response.errorMessage
       
  1568 			),
  1335 			$message     = wp.updates.adminNotice( {
  1569 			$message     = wp.updates.adminNotice( {
  1336 				className: 'update-message notice-error notice-alt',
  1570 				className: 'update-message notice-error notice-alt',
  1337 				message:   errorMessage
  1571 				message:   errorMessage
  1338 			} );
  1572 			} );
  1339 
  1573 
  1370 	 * Adds the appropriate callback based on the type of action and the current page.
  1604 	 * Adds the appropriate callback based on the type of action and the current page.
  1371 	 *
  1605 	 *
  1372 	 * @since 4.6.0
  1606 	 * @since 4.6.0
  1373 	 * @private
  1607 	 * @private
  1374 	 *
  1608 	 *
  1375 	 * @param {object} data   AJAX payload.
  1609 	 * @param {Object} data   Ajax payload.
  1376 	 * @param {string} action The type of request to perform.
  1610 	 * @param {string} action The type of request to perform.
  1377 	 * @return {object} The AJAX payload with the appropriate callbacks.
  1611 	 * @return {Object} The Ajax payload with the appropriate callbacks.
  1378 	 */
  1612 	 */
  1379 	wp.updates._addCallbacks = function( data, action ) {
  1613 	wp.updates._addCallbacks = function( data, action ) {
  1380 		if ( 'import' === pagenow && 'install-plugin' === action ) {
  1614 		if ( 'import' === pagenow && 'install-plugin' === action ) {
  1381 			data.success = wp.updates.installImporterSuccess;
  1615 			data.success = wp.updates.installImporterSuccess;
  1382 			data.error   = wp.updates.installImporterError;
  1616 			data.error   = wp.updates.installImporterError;
  1564 	/**
  1798 	/**
  1565 	 * Handles credential errors and runs events that need to happen in that case.
  1799 	 * Handles credential errors and runs events that need to happen in that case.
  1566 	 *
  1800 	 *
  1567 	 * @since 4.2.0
  1801 	 * @since 4.2.0
  1568 	 *
  1802 	 *
  1569 	 * @param {object} response Ajax response.
  1803 	 * @param {Object} response Ajax response.
  1570 	 * @param {string} action   The type of request to perform.
  1804 	 * @param {string} action   The type of request to perform.
  1571 	 */
  1805 	 */
  1572 	wp.updates.credentialError = function( response, action ) {
  1806 	wp.updates.credentialError = function( response, action ) {
  1573 
  1807 
  1574 		// Restore callbacks.
  1808 		// Restore callbacks.
  1592 	/**
  1826 	/**
  1593 	 * Handles credentials errors if it could not connect to the filesystem.
  1827 	 * Handles credentials errors if it could not connect to the filesystem.
  1594 	 *
  1828 	 *
  1595 	 * @since 4.6.0
  1829 	 * @since 4.6.0
  1596 	 *
  1830 	 *
  1597 	 * @param {object} response              Response from the server.
  1831 	 * @param {Object} response              Response from the server.
  1598 	 * @param {string} response.errorCode    Error code for the error that occurred.
  1832 	 * @param {string} response.errorCode    Error code for the error that occurred.
  1599 	 * @param {string} response.errorMessage The error that occurred.
  1833 	 * @param {string} response.errorMessage The error that occurred.
  1600 	 * @param {string} action                The type of request to perform.
  1834 	 * @param {string} action                The type of request to perform.
  1601 	 * @returns {boolean} Whether there is an error that needs to be handled or not.
  1835 	 * @return {boolean} Whether there is an error that needs to be handled or not.
  1602 	 */
  1836 	 */
  1603 	wp.updates.maybeHandleCredentialError = function( response, action ) {
  1837 	wp.updates.maybeHandleCredentialError = function( response, action ) {
  1604 		if ( wp.updates.shouldRequestFilesystemCredentials && response.errorCode && 'unable_to_connect_to_filesystem' === response.errorCode ) {
  1838 		if ( wp.updates.shouldRequestFilesystemCredentials && response.errorCode && 'unable_to_connect_to_filesystem' === response.errorCode ) {
  1605 			wp.updates.credentialError( response, action );
  1839 			wp.updates.credentialError( response, action );
  1606 			return true;
  1840 			return true;
  1608 
  1842 
  1609 		return false;
  1843 		return false;
  1610 	};
  1844 	};
  1611 
  1845 
  1612 	/**
  1846 	/**
  1613 	 * Validates an AJAX response to ensure it's a proper object.
  1847 	 * Validates an Ajax response to ensure it's a proper object.
  1614 	 *
  1848 	 *
  1615 	 * If the response deems to be invalid, an admin notice is being displayed.
  1849 	 * If the response deems to be invalid, an admin notice is being displayed.
  1616 	 *
  1850 	 *
  1617 	 * @param {(object|string)} response              Response from the server.
  1851 	 * @param {(Object|string)} response              Response from the server.
  1618 	 * @param {function=}       response.always       Optional. Callback for when the Deferred is resolved or rejected.
  1852 	 * @param {function=}       response.always       Optional. Callback for when the Deferred is resolved or rejected.
  1619 	 * @param {string=}         response.statusText   Optional. Status message corresponding to the status code.
  1853 	 * @param {string=}         response.statusText   Optional. Status message corresponding to the status code.
  1620 	 * @param {string=}         response.responseText Optional. Request response as text.
  1854 	 * @param {string=}         response.responseText Optional. Request response as text.
  1621 	 * @param {string}          action                Type of action the response is referring to. Can be 'delete',
  1855 	 * @param {string}          action                Type of action the response is referring to. Can be 'delete',
  1622 	 *                                                'update' or 'install'.
  1856 	 *                                                'update' or 'install'.
  1623 	 */
  1857 	 */
  1624 	wp.updates.isValidResponse = function( response, action ) {
  1858 	wp.updates.isValidResponse = function( response, action ) {
  1625 		var error = wp.updates.l10n.unknownError,
  1859 		var error = __( 'Something went wrong.' ),
  1626 		    errorMessage;
  1860 			errorMessage;
  1627 
  1861 
  1628 		// Make sure the response is a valid data object and not a Promise object.
  1862 		// Make sure the response is a valid data object and not a Promise object.
  1629 		if ( _.isObject( response ) && ! _.isFunction( response.always ) ) {
  1863 		if ( _.isObject( response ) && ! _.isFunction( response.always ) ) {
  1630 			return true;
  1864 			return true;
  1631 		}
  1865 		}
  1632 
  1866 
  1633 		if ( _.isString( response ) && '-1' === response ) {
  1867 		if ( _.isString( response ) && '-1' === response ) {
  1634 			error = wp.updates.l10n.nonceError;
  1868 			error = __( 'An error has occurred. Please reload the page and try again.' );
  1635 		} else if ( _.isString( response ) ) {
  1869 		} else if ( _.isString( response ) ) {
  1636 			error = response;
  1870 			error = response;
  1637 		} else if ( 'undefined' !== typeof response.readyState && 0 === response.readyState ) {
  1871 		} else if ( 'undefined' !== typeof response.readyState && 0 === response.readyState ) {
  1638 			error = wp.updates.l10n.connectionError;
  1872 			error = __( 'Connection lost or the server is busy. Please try again later.' );
  1639 		} else if ( _.isString( response.responseText ) && '' !== response.responseText ) {
  1873 		} else if ( _.isString( response.responseText ) && '' !== response.responseText ) {
  1640 			error = response.responseText;
  1874 			error = response.responseText;
  1641 		} else if ( _.isString( response.statusText ) ) {
  1875 		} else if ( _.isString( response.statusText ) ) {
  1642 			error = response.statusText;
  1876 			error = response.statusText;
  1643 		}
  1877 		}
  1644 
  1878 
  1645 		switch ( action ) {
  1879 		switch ( action ) {
  1646 			case 'update':
  1880 			case 'update':
  1647 				errorMessage = wp.updates.l10n.updateFailed;
  1881 				/* translators: %s: Error string for a failed update. */
       
  1882 				errorMessage = __( 'Update failed: %s' );
  1648 				break;
  1883 				break;
  1649 
  1884 
  1650 			case 'install':
  1885 			case 'install':
  1651 				errorMessage = wp.updates.l10n.installFailed;
  1886 				/* translators: %s: Error string for a failed installation. */
       
  1887 				errorMessage = __( 'Installation failed: %s' );
  1652 				break;
  1888 				break;
  1653 
  1889 
  1654 			case 'delete':
  1890 			case 'delete':
  1655 				errorMessage = wp.updates.l10n.deleteFailed;
  1891 				/* translators: %s: Error string for a failed deletion. */
       
  1892 				errorMessage = __( 'Deletion failed: %s' );
  1656 				break;
  1893 				break;
  1657 		}
  1894 		}
  1658 
  1895 
  1659 		// Messages are escaped, remove HTML tags to make them more readable.
  1896 		// Messages are escaped, remove HTML tags to make them more readable.
  1660 		error = error.replace( /<[\/a-z][^<>]*>/gi, '' );
  1897 		error = error.replace( /<[\/a-z][^<>]*>/gi, '' );
  1674 		// Change buttons of all running updates.
  1911 		// Change buttons of all running updates.
  1675 		$( '.button.updating-message' )
  1912 		$( '.button.updating-message' )
  1676 			.removeClass( 'updating-message' )
  1913 			.removeClass( 'updating-message' )
  1677 			.removeAttr( 'aria-label' )
  1914 			.removeAttr( 'aria-label' )
  1678 			.prop( 'disabled', true )
  1915 			.prop( 'disabled', true )
  1679 			.text( wp.updates.l10n.updateFailedShort );
  1916 			.text( __( 'Update failed.' ) );
  1680 
  1917 
  1681 		$( '.updating-message:not(.button):not(.thickbox)' )
  1918 		$( '.updating-message:not(.button):not(.thickbox)' )
  1682 			.removeClass( 'updating-message notice-warning' )
  1919 			.removeClass( 'updating-message notice-warning' )
  1683 			.addClass( 'notice-error' )
  1920 			.addClass( 'notice-error' )
  1684 			.find( 'p' )
  1921 			.find( 'p' )
  1698 	 *
  1935 	 *
  1699 	 * @since 4.2.0
  1936 	 * @since 4.2.0
  1700 	 */
  1937 	 */
  1701 	wp.updates.beforeunload = function() {
  1938 	wp.updates.beforeunload = function() {
  1702 		if ( wp.updates.ajaxLocked ) {
  1939 		if ( wp.updates.ajaxLocked ) {
  1703 			return wp.updates.l10n.beforeunload;
  1940 			return __( 'Updates may not complete if you navigate away from this page.' );
  1704 		}
  1941 		}
  1705 	};
  1942 	};
  1706 
  1943 
  1707 	$( function() {
  1944 	$( function() {
  1708 		var $pluginFilter        = $( '#plugin-filter' ),
  1945 		var $pluginFilter        = $( '#plugin-filter' ),
  1811 					.removeClass( 'updating-message' )
  2048 					.removeClass( 'updating-message' )
  1812 					.html( originalText );
  2049 					.html( originalText );
  1813 
  2050 
  1814 				if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
  2051 				if ( 'plugin-install' === pagenow || 'plugin-install-network' === pagenow ) {
  1815 					if ( 'update-plugin' === job.action ) {
  2052 					if ( 'update-plugin' === job.action ) {
  1816 						$message.attr( 'aria-label', wp.updates.l10n.pluginUpdateNowLabel.replace( '%s', $message.data( 'name' ) ) );
  2053 						$message.attr(
       
  2054 							'aria-label',
       
  2055 							sprintf(
       
  2056 								/* translators: %s: Plugin name and version. */
       
  2057 								_x( 'Update %s now', 'plugin' ),
       
  2058 								$message.data( 'name' )
       
  2059 							)
       
  2060 						);
  1817 					} else if ( 'install-plugin' === job.action ) {
  2061 					} else if ( 'install-plugin' === job.action ) {
  1818 						$message.attr( 'aria-label', wp.updates.l10n.pluginInstallNowLabel.replace( '%s', $message.data( 'name' ) ) );
  2062 						$message.attr(
       
  2063 							'aria-label',
       
  2064 							sprintf(
       
  2065 								/* translators: %s: Plugin name. */
       
  2066 								_x( 'Install %s now', 'plugin' ),
       
  2067 								$message.data( 'name' )
       
  2068 							)
       
  2069 						);
  1819 					}
  2070 					}
  1820 				}
  2071 				}
  1821 			}
  2072 			}
  1822 
  2073 
  1823 			wp.a11y.speak( wp.updates.l10n.updateCancel, 'polite' );
  2074 			wp.a11y.speak( __( 'Update canceled.' ) );
  1824 		} );
  2075 		} );
  1825 
  2076 
  1826 		/**
  2077 		/**
  1827 		 * Click handler for plugin updates in List Table view.
  2078 		 * Click handler for plugin updates in List Table view.
  1828 		 *
  2079 		 *
  1894 				$document.on( 'credential-modal-cancel', function() {
  2145 				$document.on( 'credential-modal-cancel', function() {
  1895 					var $message = $( '.install-now.updating-message' );
  2146 					var $message = $( '.install-now.updating-message' );
  1896 
  2147 
  1897 					$message
  2148 					$message
  1898 						.removeClass( 'updating-message' )
  2149 						.removeClass( 'updating-message' )
  1899 						.text( wp.updates.l10n.installNow );
  2150 						.text( __( 'Install Now' ) );
  1900 
  2151 
  1901 					wp.a11y.speak( wp.updates.l10n.updateCancel, 'polite' );
  2152 					wp.a11y.speak( __( 'Update canceled.' ) );
  1902 				} );
  2153 				} );
  1903 			}
  2154 			}
  1904 
  2155 
  1905 			wp.updates.installPlugin( {
  2156 			wp.updates.installPlugin( {
  1906 				slug: $button.data( 'slug' )
  2157 				slug: $button.data( 'slug' )
  1929 
  2180 
  1930 				$document.on( 'credential-modal-cancel', function() {
  2181 				$document.on( 'credential-modal-cancel', function() {
  1931 
  2182 
  1932 					$button
  2183 					$button
  1933 						.removeClass( 'updating-message' )
  2184 						.removeClass( 'updating-message' )
  1934 						.text( wp.updates.l10n.installNow )
  2185 						.attr(
  1935 						.attr( 'aria-label', wp.updates.l10n.pluginInstallNowLabel.replace( '%s', pluginName ) );
  2186 							'aria-label',
  1936 
  2187 							sprintf(
  1937 					wp.a11y.speak( wp.updates.l10n.updateCancel, 'polite' );
  2188 								/* translators: %s: Plugin name. */
       
  2189 								_x( 'Install %s now', 'plugin' ),
       
  2190 								pluginName
       
  2191 							)
       
  2192 						)
       
  2193 						.text( __( 'Install Now' ) );
       
  2194 
       
  2195 					wp.a11y.speak( __( 'Update canceled.' ) );
  1938 				} );
  2196 				} );
  1939 			}
  2197 			}
  1940 
  2198 
  1941 			wp.updates.installPlugin( {
  2199 			wp.updates.installPlugin( {
  1942 				slug:    $button.data( 'slug' ),
  2200 				slug:    $button.data( 'slug' ),
  1952 		 * @since 4.6.0
  2210 		 * @since 4.6.0
  1953 		 *
  2211 		 *
  1954 		 * @param {Event} event Event interface.
  2212 		 * @param {Event} event Event interface.
  1955 		 */
  2213 		 */
  1956 		$bulkActionForm.on( 'click', '[data-plugin] a.delete', function( event ) {
  2214 		$bulkActionForm.on( 'click', '[data-plugin] a.delete', function( event ) {
  1957 			var $pluginRow = $( event.target ).parents( 'tr' );
  2215 			var $pluginRow = $( event.target ).parents( 'tr' ),
       
  2216 				confirmMessage;
       
  2217 
       
  2218 			if ( $pluginRow.hasClass( 'is-uninstallable' ) ) {
       
  2219 				confirmMessage = sprintf(
       
  2220 					/* translators: %s: Plugin name. */
       
  2221 					__( 'Are you sure you want to delete %s and its data?' ),
       
  2222 					$pluginRow.find( '.plugin-title strong' ).text()
       
  2223 				);
       
  2224 			} else {
       
  2225 				confirmMessage = sprintf(
       
  2226 					/* translators: %s: Plugin name. */
       
  2227 					__( 'Are you sure you want to delete %s?' ),
       
  2228 					$pluginRow.find( '.plugin-title strong' ).text()
       
  2229 				);
       
  2230 			}
  1958 
  2231 
  1959 			event.preventDefault();
  2232 			event.preventDefault();
  1960 
  2233 
  1961 			if ( ! window.confirm( wp.updates.l10n.aysDeleteUninstall.replace( '%s', $pluginRow.find( '.plugin-title strong' ).text() ) ) ) {
  2234 			if ( ! window.confirm( confirmMessage ) ) {
  1962 				return;
  2235 				return;
  1963 			}
  2236 			}
  1964 
  2237 
  1965 			wp.updates.maybeRequestFilesystemCredentials( event );
  2238 			wp.updates.maybeRequestFilesystemCredentials( event );
  1966 
  2239 
  2003 		 * @since 4.6.0
  2276 		 * @since 4.6.0
  2004 		 *
  2277 		 *
  2005 		 * @param {Event} event Event interface.
  2278 		 * @param {Event} event Event interface.
  2006 		 */
  2279 		 */
  2007 		$document.on( 'click', '.themes-php.network-admin a.delete', function( event ) {
  2280 		$document.on( 'click', '.themes-php.network-admin a.delete', function( event ) {
  2008 			var $themeRow = $( event.target ).parents( 'tr' );
  2281 			var $themeRow = $( event.target ).parents( 'tr' ),
       
  2282 				confirmMessage = sprintf(
       
  2283 					/* translators: %s: Theme name. */
       
  2284 					__( 'Are you sure you want to delete %s?' ),
       
  2285 					$themeRow.find( '.theme-title strong' ).text()
       
  2286 				);
  2009 
  2287 
  2010 			event.preventDefault();
  2288 			event.preventDefault();
  2011 
  2289 
  2012 			if ( ! window.confirm( wp.updates.l10n.aysDelete.replace( '%s', $themeRow.find( '.theme-title strong' ).text() ) ) ) {
  2290 			if ( ! window.confirm( confirmMessage ) ) {
  2013 				return;
  2291 				return;
  2014 			}
  2292 			}
  2015 
  2293 
  2016 			wp.updates.maybeRequestFilesystemCredentials( event );
  2294 			wp.updates.maybeRequestFilesystemCredentials( event );
  2017 
  2295 
  2058 				$( 'html, body' ).animate( { scrollTop: 0 } );
  2336 				$( 'html, body' ).animate( { scrollTop: 0 } );
  2059 
  2337 
  2060 				return wp.updates.addAdminNotice( {
  2338 				return wp.updates.addAdminNotice( {
  2061 					id:        'no-items-selected',
  2339 					id:        'no-items-selected',
  2062 					className: 'notice-error is-dismissible',
  2340 					className: 'notice-error is-dismissible',
  2063 					message:   wp.updates.l10n.noItemsSelected
  2341 					message:   __( 'Please select at least one item to perform this action on.' )
  2064 				} );
  2342 				} );
  2065 			}
  2343 			}
  2066 
  2344 
  2067 			// Determine the type of request we're dealing with.
  2345 			// Determine the type of request we're dealing with.
  2068 			switch ( bulkAction ) {
  2346 			switch ( bulkAction ) {
  2069 				case 'update-selected':
  2347 				case 'update-selected':
  2070 					action = bulkAction.replace( 'selected', type );
  2348 					action = bulkAction.replace( 'selected', type );
  2071 					break;
  2349 					break;
  2072 
  2350 
  2073 				case 'delete-selected':
  2351 				case 'delete-selected':
  2074 					if ( ! window.confirm( 'plugin' === type ? wp.updates.l10n.aysBulkDelete : wp.updates.l10n.aysBulkDeleteThemes ) ) {
  2352 					var confirmMessage = 'plugin' === type ?
       
  2353 						__( 'Are you sure you want to delete the selected plugins and their data?' ) :
       
  2354 						__( 'Caution: These themes may be active on other sites in the network. Are you sure you want to proceed?' );
       
  2355 
       
  2356 					if ( ! window.confirm( confirmMessage ) ) {
  2075 						event.preventDefault();
  2357 						event.preventDefault();
  2076 						return;
  2358 						return;
  2077 					}
  2359 					}
  2078 
  2360 
  2079 					action = bulkAction.replace( 'selected', type );
  2361 					action = bulkAction.replace( 'selected', type );
  2206 			if ( ! $searchTab.length ) {
  2488 			if ( ! $searchTab.length ) {
  2207 				$searchTab = $( '<li class="plugin-install-search" />' )
  2489 				$searchTab = $( '<li class="plugin-install-search" />' )
  2208 					.append( $( '<a />', {
  2490 					.append( $( '<a />', {
  2209 						'class': 'current',
  2491 						'class': 'current',
  2210 						'href': searchLocation,
  2492 						'href': searchLocation,
  2211 						'text': wp.updates.l10n.searchResultsLabel
  2493 						'text': __( 'Search Results' )
  2212 					} ) );
  2494 					} ) );
  2213 
  2495 
  2214 				$( '.wp-filter .filter-links .current' )
  2496 				$( '.wp-filter .filter-links .current' )
  2215 					.removeClass( 'current' )
  2497 					.removeClass( 'current' )
  2216 					.parents( '.filter-links' )
  2498 					.parents( '.filter-links' )
  2229 				$( 'body' ).removeClass( 'loading-content' );
  2511 				$( 'body' ).removeClass( 'loading-content' );
  2230 				$pluginFilter.append( response.items );
  2512 				$pluginFilter.append( response.items );
  2231 				delete wp.updates.searchRequest;
  2513 				delete wp.updates.searchRequest;
  2232 
  2514 
  2233 				if ( 0 === response.count ) {
  2515 				if ( 0 === response.count ) {
  2234 					wp.a11y.speak( wp.updates.l10n.noPluginsFound );
  2516 					wp.a11y.speak( __( 'You do not appear to have any plugins available at this time.' ) );
  2235 				} else {
  2517 				} else {
  2236 					wp.a11y.speak( wp.updates.l10n.pluginsFound.replace( '%d', response.count ) );
  2518 					wp.a11y.speak(
       
  2519 						sprintf(
       
  2520 							/* translators: %s: Number of plugins. */
       
  2521 							__( 'Number of plugins found: %d' ),
       
  2522 							response.count
       
  2523 						)
       
  2524 					);
  2237 				}
  2525 				}
  2238 			} );
  2526 			} );
  2239 		}, 500 ) );
  2527 		}, 1000 ) );
  2240 
  2528 
  2241 		if ( $pluginSearch.length ) {
  2529 		if ( $pluginSearch.length ) {
  2242 			$pluginSearch.attr( 'aria-describedby', 'live-search-desc' );
  2530 			$pluginSearch.attr( 'aria-describedby', 'live-search-desc' );
  2243 		}
  2531 		}
  2244 
  2532 
  2287 			$( '.subsubsub .current' ).removeClass( 'current' );
  2575 			$( '.subsubsub .current' ).removeClass( 'current' );
  2288 
  2576 
  2289 			wp.updates.searchRequest = wp.ajax.post( 'search-plugins', data ).done( function( response ) {
  2577 			wp.updates.searchRequest = wp.ajax.post( 'search-plugins', data ).done( function( response ) {
  2290 
  2578 
  2291 				// Can we just ditch this whole subtitle business?
  2579 				// Can we just ditch this whole subtitle business?
  2292 				var $subTitle    = $( '<span />' ).addClass( 'subtitle' ).html( wp.updates.l10n.searchResults.replace( '%s', _.escape( data.s ) ) ),
  2580 				var $subTitle    = $( '<span />' ).addClass( 'subtitle' ).html(
       
  2581 					sprintf(
       
  2582 						/* translators: %s: Search query. */
       
  2583 						__( 'Search results for &#8220;%s&#8221;' ),
       
  2584 						_.escape( data.s )
       
  2585 					) ),
  2293 					$oldSubTitle = $( '.wrap .subtitle' );
  2586 					$oldSubTitle = $( '.wrap .subtitle' );
  2294 
  2587 
  2295 				if ( ! data.s.length ) {
  2588 				if ( ! data.s.length ) {
  2296 					$oldSubTitle.remove();
  2589 					$oldSubTitle.remove();
  2297 					$( '.subsubsub .' + data.plugin_status + ' a' ).addClass( 'current' );
  2590 					$( '.subsubsub .' + data.plugin_status + ' a' ).addClass( 'current' );
  2304 				$( 'body' ).removeClass( 'loading-content' );
  2597 				$( 'body' ).removeClass( 'loading-content' );
  2305 				$bulkActionForm.append( response.items );
  2598 				$bulkActionForm.append( response.items );
  2306 				delete wp.updates.searchRequest;
  2599 				delete wp.updates.searchRequest;
  2307 
  2600 
  2308 				if ( 0 === response.count ) {
  2601 				if ( 0 === response.count ) {
  2309 					wp.a11y.speak( wp.updates.l10n.noPluginsFound );
  2602 					wp.a11y.speak( __( 'No plugins found. Try a different search.'  ) );
  2310 				} else {
  2603 				} else {
  2311 					wp.a11y.speak( wp.updates.l10n.pluginsFound.replace( '%d', response.count ) );
  2604 					wp.a11y.speak(
       
  2605 						sprintf(
       
  2606 							/* translators: %s: Number of plugins. */
       
  2607 							__( 'Number of plugins found: %d' ),
       
  2608 							response.count
       
  2609 						)
       
  2610 					);
  2312 				}
  2611 				}
  2313 			} );
  2612 			} );
  2314 		}, 500 ) );
  2613 		}, 500 ) );
  2315 
  2614 
  2316 		/**
  2615 		/**
  2414 		 *
  2713 		 *
  2415 		 * @param {Event} event Event interface.
  2714 		 * @param {Event} event Event interface.
  2416 		 */
  2715 		 */
  2417 		$( window ).on( 'message', function( event ) {
  2716 		$( window ).on( 'message', function( event ) {
  2418 			var originalEvent  = event.originalEvent,
  2717 			var originalEvent  = event.originalEvent,
  2419 				expectedOrigin = document.location.protocol + '//' + document.location.hostname,
  2718 				expectedOrigin = document.location.protocol + '//' + document.location.host,
  2420 				message;
  2719 				message;
  2421 
  2720 
  2422 			if ( originalEvent.origin !== expectedOrigin ) {
  2721 			if ( originalEvent.origin !== expectedOrigin ) {
  2423 				return;
  2722 				return;
  2424 			}
  2723 			}
  2459 		 * Adds a callback to display a warning before leaving the page.
  2758 		 * Adds a callback to display a warning before leaving the page.
  2460 		 *
  2759 		 *
  2461 		 * @since 4.2.0
  2760 		 * @since 4.2.0
  2462 		 */
  2761 		 */
  2463 		$( window ).on( 'beforeunload', wp.updates.beforeunload );
  2762 		$( window ).on( 'beforeunload', wp.updates.beforeunload );
       
  2763 
       
  2764 		/**
       
  2765 		 * Prevents the page form scrolling when activating auto-updates with the Spacebar key.
       
  2766 		 *
       
  2767 		 * @since 5.5.0
       
  2768 		 */
       
  2769 		$document.on( 'keydown', '.column-auto-updates .toggle-auto-update, .theme-overlay .toggle-auto-update', function( event ) {
       
  2770 			if ( 32 === event.which ) {
       
  2771 				event.preventDefault();
       
  2772 			}
       
  2773 		} );
       
  2774 
       
  2775 		/**
       
  2776 		 * Click and keyup handler for enabling and disabling plugin and theme auto-updates.
       
  2777 		 *
       
  2778 		 * These controls can be either links or buttons. When JavaScript is enabled,
       
  2779 		 * we want them to behave like buttons. An ARIA role `button` is added via
       
  2780 		 * the JavaScript that targets elements with the CSS class `aria-button-if-js`.
       
  2781 		 *
       
  2782 		 * @since 5.5.0
       
  2783 		 */
       
  2784 		$document.on( 'click keyup', '.column-auto-updates .toggle-auto-update, .theme-overlay .toggle-auto-update', function( event ) {
       
  2785 			var data, asset, type, $parent,
       
  2786 				$toggler = $( this ),
       
  2787 				action = $toggler.attr( 'data-wp-action' ),
       
  2788 				$label = $toggler.find( '.label' );
       
  2789 
       
  2790 			if ( 'keyup' === event.type && 32 !== event.which ) {
       
  2791 				return;
       
  2792 			}
       
  2793 
       
  2794 			if ( 'themes' !== pagenow ) {
       
  2795 				$parent = $toggler.closest( '.column-auto-updates' );
       
  2796 			} else {
       
  2797 				$parent = $toggler.closest( '.theme-autoupdate' );
       
  2798 			}
       
  2799 
       
  2800 			event.preventDefault();
       
  2801 
       
  2802 			// Prevent multiple simultaneous requests.
       
  2803 			if ( $toggler.attr( 'data-doing-ajax' ) === 'yes' ) {
       
  2804 				return;
       
  2805 			}
       
  2806 
       
  2807 			$toggler.attr( 'data-doing-ajax', 'yes' );
       
  2808 
       
  2809 			switch ( pagenow ) {
       
  2810 				case 'plugins':
       
  2811 				case 'plugins-network':
       
  2812 					type = 'plugin';
       
  2813 					asset = $toggler.closest( 'tr' ).attr( 'data-plugin' );
       
  2814 					break;
       
  2815 				case 'themes-network':
       
  2816 					type = 'theme';
       
  2817 					asset = $toggler.closest( 'tr' ).attr( 'data-slug' );
       
  2818 					break;
       
  2819 				case 'themes':
       
  2820 					type = 'theme';
       
  2821 					asset = $toggler.attr( 'data-slug' );
       
  2822 					break;
       
  2823 			}
       
  2824 
       
  2825 			// Clear any previous errors.
       
  2826 			$parent.find( '.notice.notice-error' ).addClass( 'hidden' );
       
  2827 
       
  2828 			// Show loading status.
       
  2829 			if ( 'enable' === action ) {
       
  2830 				$label.text( __( 'Enabling...' ) );
       
  2831 			} else {
       
  2832 				$label.text( __( 'Disabling...' ) );
       
  2833 			}
       
  2834 
       
  2835 			$toggler.find( '.dashicons-update' ).removeClass( 'hidden' );
       
  2836 
       
  2837 			data = {
       
  2838 				action: 'toggle-auto-updates',
       
  2839 				_ajax_nonce: settings.ajax_nonce,
       
  2840 				state: action,
       
  2841 				type: type,
       
  2842 				asset: asset
       
  2843 			};
       
  2844 
       
  2845 			$.post( window.ajaxurl, data )
       
  2846 				.done( function( response ) {
       
  2847 					var $enabled, $disabled, enabledNumber, disabledNumber, errorMessage,
       
  2848 						href = $toggler.attr( 'href' );
       
  2849 
       
  2850 					if ( ! response.success ) {
       
  2851 						// if WP returns 0 for response (which can happen in a few cases),
       
  2852 						// output the general error message since we won't have response.data.error.
       
  2853 						if ( response.data && response.data.error ) {
       
  2854 							errorMessage = response.data.error;
       
  2855 						} else {
       
  2856 							errorMessage = __( 'The request could not be completed.' );
       
  2857 						}
       
  2858 
       
  2859 						$parent.find( '.notice.notice-error' ).removeClass( 'hidden' ).find( 'p' ).text( errorMessage );
       
  2860 						wp.a11y.speak( errorMessage, 'assertive' );
       
  2861 						return;
       
  2862 					}
       
  2863 
       
  2864 					// Update the counts in the enabled/disabled views if on a screen
       
  2865 					// with a list table.
       
  2866 					if ( 'themes' !== pagenow ) {
       
  2867 						$enabled       = $( '.auto-update-enabled span' );
       
  2868 						$disabled      = $( '.auto-update-disabled span' );
       
  2869 						enabledNumber  = parseInt( $enabled.text().replace( /[^\d]+/g, '' ), 10 ) || 0;
       
  2870 						disabledNumber = parseInt( $disabled.text().replace( /[^\d]+/g, '' ), 10 ) || 0;
       
  2871 
       
  2872 						switch ( action ) {
       
  2873 							case 'enable':
       
  2874 								++enabledNumber;
       
  2875 								--disabledNumber;
       
  2876 								break;
       
  2877 							case 'disable':
       
  2878 								--enabledNumber;
       
  2879 								++disabledNumber;
       
  2880 								break;
       
  2881 						}
       
  2882 
       
  2883 						enabledNumber = Math.max( 0, enabledNumber );
       
  2884 						disabledNumber = Math.max( 0, disabledNumber );
       
  2885 
       
  2886 						$enabled.text( '(' + enabledNumber + ')' );
       
  2887 						$disabled.text( '(' + disabledNumber + ')' );
       
  2888 					}
       
  2889 
       
  2890 					if ( 'enable' === action ) {
       
  2891 						// The toggler control can be either a link or a button.
       
  2892 						if ( $toggler[ 0 ].hasAttribute( 'href' ) ) {
       
  2893 							href = href.replace( 'action=enable-auto-update', 'action=disable-auto-update' );
       
  2894 							$toggler.attr( 'href', href );
       
  2895 						}
       
  2896 						$toggler.attr( 'data-wp-action', 'disable' );
       
  2897 
       
  2898 						$label.text( __( 'Disable auto-updates' ) );
       
  2899 						$parent.find( '.auto-update-time' ).removeClass( 'hidden' );
       
  2900 						wp.a11y.speak( __( 'Auto-updates enabled' ) );
       
  2901 					} else {
       
  2902 						// The toggler control can be either a link or a button.
       
  2903 						if ( $toggler[ 0 ].hasAttribute( 'href' ) ) {
       
  2904 							href = href.replace( 'action=disable-auto-update', 'action=enable-auto-update' );
       
  2905 							$toggler.attr( 'href', href );
       
  2906 						}
       
  2907 						$toggler.attr( 'data-wp-action', 'enable' );
       
  2908 
       
  2909 						$label.text( __( 'Enable auto-updates' ) );
       
  2910 						$parent.find( '.auto-update-time' ).addClass( 'hidden' );
       
  2911 						wp.a11y.speak( __( 'Auto-updates disabled' ) );
       
  2912 					}
       
  2913 
       
  2914 					$document.trigger( 'wp-auto-update-setting-changed', { state: action, type: type, asset: asset } );
       
  2915 				} )
       
  2916 				.fail( function() {
       
  2917 					$parent.find( '.notice.notice-error' )
       
  2918 						.removeClass( 'hidden' )
       
  2919 						.find( 'p' )
       
  2920 						.text( __( 'The request could not be completed.' ) );
       
  2921 
       
  2922 					wp.a11y.speak( __( 'The request could not be completed.' ), 'assertive' );
       
  2923 				} )
       
  2924 				.always( function() {
       
  2925 					$toggler.removeAttr( 'data-doing-ajax' ).find( '.dashicons-update' ).addClass( 'hidden' );
       
  2926 				} );
       
  2927 			}
       
  2928 		);
  2464 	} );
  2929 	} );
  2465 })( jQuery, window.wp, window._wpUpdatesSettings );
  2930 })( jQuery, window.wp, window._wpUpdatesSettings );