wp/wp-admin/js/user-profile.js
changeset 7 cf61fcea0001
parent 5 5e2f62d02dcd
child 9 177826044cd9
--- a/wp/wp-admin/js/user-profile.js	Tue Jun 09 11:14:17 2015 +0000
+++ b/wp/wp-admin/js/user-profile.js	Mon Oct 14 17:39:30 2019 +0200
@@ -1,18 +1,297 @@
-/* global ajaxurl, pwsL10n */
-(function($){
+/* global ajaxurl, pwsL10n, userProfileL10n */
+(function($) {
+	var updateLock = false,
+
+		$pass1Row,
+		$pass1Wrap,
+		$pass1,
+		$pass1Text,
+		$pass1Label,
+		$pass2,
+		$weakRow,
+		$weakCheckbox,
+		$toggleButton,
+		$submitButtons,
+		$submitButton,
+		currentPass,
+		inputEvent;
+
+	/*
+	 * Use feature detection to determine whether password inputs should use
+	 * the `keyup` or `input` event. Input is preferred but lacks support
+	 * in legacy browsers.
+	 */
+	if ( 'oninput' in document.createElement( 'input' ) ) {
+		inputEvent = 'input';
+	} else {
+		inputEvent = 'keyup';
+	}
+
+	function generatePassword() {
+		if ( typeof zxcvbn !== 'function' ) {
+			setTimeout( generatePassword, 50 );
+			return;
+		} else if ( ! $pass1.val() ) {
+			// zxcvbn loaded before user entered password.
+			$pass1.val( $pass1.data( 'pw' ) );
+			$pass1.trigger( 'pwupdate' );
+			showOrHideWeakPasswordCheckbox();
+		}
+		else {
+			// zxcvbn loaded after the user entered password, check strength.
+			check_pass_strength();
+			showOrHideWeakPasswordCheckbox();
+		}
+
+		if ( 1 !== parseInt( $toggleButton.data( 'start-masked' ), 10 ) ) {
+			$pass1Wrap.addClass( 'show-password' );
+		} else {
+			$toggleButton.trigger( 'click' );
+		}
+
+		// Once zxcvbn loads, passwords strength is known.
+		$( '#pw-weak-text-label' ).html( userProfileL10n.warnWeak );
+	}
+
+	function bindPass1() {
+		currentPass = $pass1.val();
+
+		$pass1Wrap = $pass1.parent();
+
+		$pass1Text = $( '<input type="text"/>' )
+			.attr( {
+				'id':           'pass1-text',
+				'name':         'pass1-text',
+				'autocomplete': 'off'
+			} )
+			.addClass( $pass1[0].className )
+			.data( 'pw', $pass1.data( 'pw' ) )
+			.val( $pass1.val() )
+			.on( inputEvent, function () {
+				if ( $pass1Text.val() === currentPass ) {
+					return;
+				}
+				$pass2.val( $pass1Text.val() );
+				$pass1.val( $pass1Text.val() ).trigger( 'pwupdate' );
+				currentPass = $pass1Text.val();
+			} );
+
+		$pass1.after( $pass1Text );
+
+		if ( 1 === parseInt( $pass1.data( 'reveal' ), 10 ) ) {
+			generatePassword();
+		}
+
+		$pass1.on( inputEvent + ' pwupdate', function () {
+			if ( $pass1.val() === currentPass ) {
+				return;
+			}
+
+			currentPass = $pass1.val();
+			if ( $pass1Text.val() !== currentPass ) {
+				$pass1Text.val( currentPass );
+			}
+			$pass1.add( $pass1Text ).removeClass( 'short bad good strong' );
+			showOrHideWeakPasswordCheckbox();
+		} );
+	}
+
+	function resetToggle() {
+		$toggleButton
+			.data( 'toggle', 0 )
+			.attr({
+				'aria-label': userProfileL10n.ariaHide
+			})
+			.find( '.text' )
+				.text( userProfileL10n.hide )
+			.end()
+			.find( '.dashicons' )
+				.removeClass( 'dashicons-visibility' )
+				.addClass( 'dashicons-hidden' );
+
+		$pass1Text.focus();
+
+		$pass1Label.attr( 'for', 'pass1-text' );
+	}
+
+	function bindToggleButton() {
+		$toggleButton = $pass1Row.find('.wp-hide-pw');
+		$toggleButton.show().on( 'click', function () {
+			if ( 1 === parseInt( $toggleButton.data( 'toggle' ), 10 ) ) {
+				$pass1Wrap.addClass( 'show-password' );
+
+				resetToggle();
+
+				if ( ! _.isUndefined( $pass1Text[0].setSelectionRange ) ) {
+					$pass1Text[0].setSelectionRange( 0, 100 );
+				}
+			} else {
+				$pass1Wrap.removeClass( 'show-password' );
+				$toggleButton
+					.data( 'toggle', 1 )
+					.attr({
+						'aria-label': userProfileL10n.ariaShow
+					})
+					.find( '.text' )
+						.text( userProfileL10n.show )
+					.end()
+					.find( '.dashicons' )
+						.removeClass('dashicons-hidden')
+						.addClass('dashicons-visibility');
+
+				$pass1.focus();
+
+				$pass1Label.attr( 'for', 'pass1' );
+
+				if ( ! _.isUndefined( $pass1[0].setSelectionRange ) ) {
+					$pass1[0].setSelectionRange( 0, 100 );
+				}
+			}
+		});
+	}
+
+	function bindPasswordForm() {
+		var $passwordWrapper,
+			$generateButton,
+			$cancelButton;
+
+		$pass1Row = $('.user-pass1-wrap');
+		$pass1Label = $pass1Row.find('th label').attr( 'for', 'pass1-text' );
+
+		// hide this
+		$('.user-pass2-wrap').hide();
+
+		$submitButton = $( '#submit, #wp-submit' ).on( 'click', function () {
+			updateLock = false;
+		});
+
+		$submitButtons = $submitButton.add( ' #createusersub' );
+
+		$weakRow = $( '.pw-weak' );
+		$weakCheckbox = $weakRow.find( '.pw-checkbox' );
+		$weakCheckbox.change( function() {
+			$submitButtons.prop( 'disabled', ! $weakCheckbox.prop( 'checked' ) );
+		} );
+
+		$pass1 = $('#pass1');
+		if ( $pass1.length ) {
+			bindPass1();
+		}
+
+		/**
+		 * Fix a LastPass mismatch issue, LastPass only changes pass2.
+		 *
+		 * This fixes the issue by copying any changes from the hidden
+		 * pass2 field to the pass1 field, then running check_pass_strength.
+		 */
+		$pass2 = $('#pass2').on( inputEvent, function () {
+			if ( $pass2.val().length > 0 ) {
+				$pass1.val( $pass2.val() );
+				$pass2.val('');
+				currentPass = '';
+				$pass1.trigger( 'pwupdate' );
+			}
+		} );
+
+		// Disable hidden inputs to prevent autofill and submission.
+		if ( $pass1.is( ':hidden' ) ) {
+			$pass1.prop( 'disabled', true );
+			$pass2.prop( 'disabled', true );
+			$pass1Text.prop( 'disabled', true );
+		}
+
+		$passwordWrapper = $pass1Row.find( '.wp-pwd' );
+		$generateButton  = $pass1Row.find( 'button.wp-generate-pw' );
+
+		bindToggleButton();
+
+		if ( $generateButton.length ) {
+			$passwordWrapper.hide();
+		}
+
+		$generateButton.show();
+		$generateButton.on( 'click', function () {
+			updateLock = true;
+
+			$generateButton.hide();
+			$passwordWrapper.show();
+
+			// Enable the inputs when showing.
+			$pass1.attr( 'disabled', false );
+			$pass2.attr( 'disabled', false );
+			$pass1Text.attr( 'disabled', false );
+
+			if ( $pass1Text.val().length === 0 ) {
+				generatePassword();
+			}
+
+			_.defer( function() {
+				$pass1Text.focus();
+				if ( ! _.isUndefined( $pass1Text[0].setSelectionRange ) ) {
+					$pass1Text[0].setSelectionRange( 0, 100 );
+				}
+			}, 0 );
+		} );
+
+		$cancelButton = $pass1Row.find( 'button.wp-cancel-pw' );
+		$cancelButton.on( 'click', function () {
+			updateLock = false;
+
+			// Clear any entered password.
+			$pass1Text.val( '' );
+
+			// Generate a new password.
+			wp.ajax.post( 'generate-password' )
+				.done( function( data ) {
+					$pass1.data( 'pw', data );
+				} );
+
+			$generateButton.show();
+			$passwordWrapper.hide();
+
+			$weakRow.hide( 0, function () {
+				$weakCheckbox.removeProp( 'checked' );
+			} );
+
+			// Disable the inputs when hiding to prevent autofill and submission.
+			$pass1.prop( 'disabled', true );
+			$pass2.prop( 'disabled', true );
+			$pass1Text.prop( 'disabled', true );
+
+			resetToggle();
+
+			if ( $pass1Row.closest( 'form' ).is( '#your-profile' ) ) {
+				// Clear password field to prevent update
+				$pass1.val( '' ).trigger( 'pwupdate' );
+				$submitButtons.prop( 'disabled', false );
+			}
+		} );
+
+		$pass1Row.closest( 'form' ).on( 'submit', function () {
+			updateLock = false;
+
+			$pass1.prop( 'disabled', false );
+			$pass2.prop( 'disabled', false );
+			$pass2.val( $pass1.val() );
+			$pass1Wrap.removeClass( 'show-password' );
+		});
+	}
 
 	function check_pass_strength() {
-		var pass1 = $('#pass1').val(), pass2 = $('#pass2').val(), strength;
+		var pass1 = $('#pass1').val(), strength;
 
 		$('#pass-strength-result').removeClass('short bad good strong');
 		if ( ! pass1 ) {
-			$('#pass-strength-result').html( pwsL10n.empty );
+			$('#pass-strength-result').html( '&nbsp;' );
 			return;
 		}
 
-		strength = wp.passwordStrength.meter( pass1, wp.passwordStrength.userInputBlacklist(), pass2 );
+		strength = wp.passwordStrength.meter( pass1, wp.passwordStrength.userInputBlacklist(), pass1 );
 
 		switch ( strength ) {
+			case -1:
+				$( '#pass-strength-result' ).addClass( 'bad' ).html( pwsL10n.unknown );
+				break;
 			case 2:
 				$('#pass-strength-result').addClass('bad').html( pwsL10n.bad );
 				break;
@@ -30,12 +309,30 @@
 		}
 	}
 
+	function showOrHideWeakPasswordCheckbox() {
+		var passStrength = $('#pass-strength-result')[0];
+
+		if ( passStrength.className ) {
+			$pass1.add( $pass1Text ).addClass( passStrength.className );
+			if ( $( passStrength ).is( '.short, .bad' ) ) {
+				if ( ! $weakCheckbox.prop( 'checked' ) ) {
+					$submitButtons.prop( 'disabled', true );
+				}
+				$weakRow.show();
+			} else {
+				$submitButtons.prop( 'disabled', false );
+				$weakRow.hide();
+			}
+		}
+	}
+
 	$(document).ready( function() {
 		var $colorpicker, $stylesheet, user_id, current_user_id,
-			select = $( '#display_name' );
+			select       = $( '#display_name' ),
+			current_name = select.val(),
+			greeting     = $( '#wp-admin-bar-my-account' ).find( '.display-name' );
 
-		$('#pass1').val('').on( 'input propertychange', check_pass_strength );
-		$('#pass2').val('').on( 'input propertychange', check_pass_strength );
+		$('#pass1').val('').on( inputEvent + ' pwupdate', check_pass_strength );
 		$('#pass-strength-result').show();
 		$('.color-palette').click( function() {
 			$(this).siblings('input[name="admin_color"]').prop('checked', true);
@@ -75,6 +372,19 @@
 					}
 				});
 			});
+
+			/**
+			 * Replaces "Howdy, *" in the admin toolbar whenever the display name dropdown is updated for one's own profile.
+			 */
+			select.on( 'change', function() {
+				if ( user_id !== current_user_id ) {
+					return;
+				}
+
+				var display_name = $.trim( this.value ) || current_name;
+
+				greeting.text( display_name );
+			} );
 		}
 
 		$colorpicker = $( '#color-picker' );
@@ -126,6 +436,8 @@
 				});
 			}
 		});
+
+		bindPasswordForm();
 	});
 
 	$( '#destroy-sessions' ).on( 'click', function( e ) {
@@ -146,4 +458,13 @@
 		e.preventDefault();
 	});
 
+	window.generatePassword = generatePassword;
+
+	/* Warn the user if password was generated but not saved */
+	$( window ).on( 'beforeunload', function () {
+		if ( true === updateLock ) {
+			return userProfileL10n.warn;
+		}
+	} );
+
 })(jQuery);