1 /* global ajaxurl, pwsL10n, userProfileL10n */
3 var updateLock = false,
20 * Use feature detection to determine whether password inputs should use
21 * the `keyup` or `input` event. Input is preferred but lacks support
24 if ( 'oninput' in document.createElement( 'input' ) ) {
30 function generatePassword() {
31 if ( typeof zxcvbn !== 'function' ) {
32 setTimeout( generatePassword, 50 );
34 } else if ( ! $pass1.val() ) {
35 // zxcvbn loaded before user entered password.
36 $pass1.val( $pass1.data( 'pw' ) );
37 $pass1.trigger( 'pwupdate' );
38 showOrHideWeakPasswordCheckbox();
41 // zxcvbn loaded after the user entered password, check strength.
42 check_pass_strength();
43 showOrHideWeakPasswordCheckbox();
46 if ( 1 !== parseInt( $toggleButton.data( 'start-masked' ), 10 ) ) {
47 $pass1Wrap.addClass( 'show-password' );
49 $toggleButton.trigger( 'click' );
52 // Once zxcvbn loads, passwords strength is known.
53 $( '#pw-weak-text-label' ).html( userProfileL10n.warnWeak );
56 function bindPass1() {
57 currentPass = $pass1.val();
59 $pass1Wrap = $pass1.parent();
61 $pass1Text = $( '<input type="text"/>' )
67 .addClass( $pass1[0].className )
68 .data( 'pw', $pass1.data( 'pw' ) )
70 .on( inputEvent, function () {
71 if ( $pass1Text.val() === currentPass ) {
74 $pass2.val( $pass1Text.val() );
75 $pass1.val( $pass1Text.val() ).trigger( 'pwupdate' );
76 currentPass = $pass1Text.val();
79 $pass1.after( $pass1Text );
81 if ( 1 === parseInt( $pass1.data( 'reveal' ), 10 ) ) {
85 $pass1.on( inputEvent + ' pwupdate', function () {
86 if ( $pass1.val() === currentPass ) {
90 currentPass = $pass1.val();
91 if ( $pass1Text.val() !== currentPass ) {
92 $pass1Text.val( currentPass );
94 $pass1.add( $pass1Text ).removeClass( 'short bad good strong' );
95 showOrHideWeakPasswordCheckbox();
99 function resetToggle() {
103 'aria-label': userProfileL10n.ariaHide
106 .text( userProfileL10n.hide )
108 .find( '.dashicons' )
109 .removeClass( 'dashicons-visibility' )
110 .addClass( 'dashicons-hidden' );
114 $pass1Label.attr( 'for', 'pass1-text' );
117 function bindToggleButton() {
118 $toggleButton = $pass1Row.find('.wp-hide-pw');
119 $toggleButton.show().on( 'click', function () {
120 if ( 1 === parseInt( $toggleButton.data( 'toggle' ), 10 ) ) {
121 $pass1Wrap.addClass( 'show-password' );
125 if ( ! _.isUndefined( $pass1Text[0].setSelectionRange ) ) {
126 $pass1Text[0].setSelectionRange( 0, 100 );
129 $pass1Wrap.removeClass( 'show-password' );
133 'aria-label': userProfileL10n.ariaShow
136 .text( userProfileL10n.show )
138 .find( '.dashicons' )
139 .removeClass('dashicons-hidden')
140 .addClass('dashicons-visibility');
144 $pass1Label.attr( 'for', 'pass1' );
146 if ( ! _.isUndefined( $pass1[0].setSelectionRange ) ) {
147 $pass1[0].setSelectionRange( 0, 100 );
153 function bindPasswordForm() {
154 var $passwordWrapper,
158 $pass1Row = $('.user-pass1-wrap');
159 $pass1Label = $pass1Row.find('th label').attr( 'for', 'pass1-text' );
162 $('.user-pass2-wrap').hide();
164 $submitButton = $( '#submit' ).on( 'click', function () {
168 $submitButtons = $submitButton.add( ' #createusersub' );
170 $weakRow = $( '.pw-weak' );
171 $weakCheckbox = $weakRow.find( '.pw-checkbox' );
172 $weakCheckbox.change( function() {
173 $submitButtons.prop( 'disabled', ! $weakCheckbox.prop( 'checked' ) );
176 $pass1 = $('#pass1');
177 if ( $pass1.length ) {
182 * Fix a LastPass mismatch issue, LastPass only changes pass2.
184 * This fixes the issue by copying any changes from the hidden
185 * pass2 field to the pass1 field, then running check_pass_strength.
187 $pass2 = $('#pass2').on( inputEvent, function () {
188 if ( $pass2.val().length > 0 ) {
189 $pass1.val( $pass2.val() );
192 $pass1.trigger( 'pwupdate' );
196 // Disable hidden inputs to prevent autofill and submission.
197 if ( $pass1.is( ':hidden' ) ) {
198 $pass1.prop( 'disabled', true );
199 $pass2.prop( 'disabled', true );
200 $pass1Text.prop( 'disabled', true );
203 $passwordWrapper = $pass1Row.find( '.wp-pwd' );
204 $generateButton = $pass1Row.find( 'button.wp-generate-pw' );
208 if ( $generateButton.length ) {
209 $passwordWrapper.hide();
212 $generateButton.show();
213 $generateButton.on( 'click', function () {
216 $generateButton.hide();
217 $passwordWrapper.show();
219 // Enable the inputs when showing.
220 $pass1.attr( 'disabled', false );
221 $pass2.attr( 'disabled', false );
222 $pass1Text.attr( 'disabled', false );
224 if ( $pass1Text.val().length === 0 ) {
228 _.defer( function() {
230 if ( ! _.isUndefined( $pass1Text[0].setSelectionRange ) ) {
231 $pass1Text[0].setSelectionRange( 0, 100 );
236 $cancelButton = $pass1Row.find( 'button.wp-cancel-pw' );
237 $cancelButton.on( 'click', function () {
240 // Clear any entered password.
241 $pass1Text.val( '' );
243 // Generate a new password.
244 wp.ajax.post( 'generate-password' )
245 .done( function( data ) {
246 $pass1.data( 'pw', data );
249 $generateButton.show();
250 $passwordWrapper.hide();
252 $weakRow.hide( 0, function () {
253 $weakCheckbox.removeProp( 'checked' );
256 // Disable the inputs when hiding to prevent autofill and submission.
257 $pass1.prop( 'disabled', true );
258 $pass2.prop( 'disabled', true );
259 $pass1Text.prop( 'disabled', true );
263 if ( $pass1Row.closest( 'form' ).is( '#your-profile' ) ) {
264 // Clear password field to prevent update
265 $pass1.val( '' ).trigger( 'pwupdate' );
266 $submitButtons.prop( 'disabled', false );
270 $pass1Row.closest( 'form' ).on( 'submit', function () {
273 $pass1.prop( 'disabled', false );
274 $pass2.prop( 'disabled', false );
275 $pass2.val( $pass1.val() );
276 $pass1Wrap.removeClass( 'show-password' );
280 function check_pass_strength() {
281 var pass1 = $('#pass1').val(), strength;
283 $('#pass-strength-result').removeClass('short bad good strong');
285 $('#pass-strength-result').html( ' ' );
289 strength = wp.passwordStrength.meter( pass1, wp.passwordStrength.userInputBlacklist(), pass1 );
291 switch ( strength ) {
293 $( '#pass-strength-result' ).addClass( 'bad' ).html( pwsL10n.unknown );
296 $('#pass-strength-result').addClass('bad').html( pwsL10n.bad );
299 $('#pass-strength-result').addClass('good').html( pwsL10n.good );
302 $('#pass-strength-result').addClass('strong').html( pwsL10n.strong );
305 $('#pass-strength-result').addClass('short').html( pwsL10n.mismatch );
308 $('#pass-strength-result').addClass('short').html( pwsL10n['short'] );
312 function showOrHideWeakPasswordCheckbox() {
313 var passStrength = $('#pass-strength-result')[0];
315 if ( passStrength.className ) {
316 $pass1.add( $pass1Text ).addClass( passStrength.className );
317 if ( 'short' === passStrength.className || 'bad' === passStrength.className ) {
318 if ( ! $weakCheckbox.prop( 'checked' ) ) {
319 $submitButtons.prop( 'disabled', true );
323 $submitButtons.prop( 'disabled', false );
329 $(document).ready( function() {
330 var $colorpicker, $stylesheet, user_id, current_user_id,
331 select = $( '#display_name' );
333 $('#pass1').val('').on( inputEvent + ' pwupdate', check_pass_strength );
334 $('#pass-strength-result').show();
335 $('.color-palette').click( function() {
336 $(this).siblings('input[name="admin_color"]').prop('checked', true);
339 if ( select.length ) {
340 $('#first_name, #last_name, #nickname').bind( 'blur.user_profile', function() {
343 display_nickname : $('#nickname').val() || '',
344 display_username : $('#user_login').val() || '',
345 display_firstname : $('#first_name').val() || '',
346 display_lastname : $('#last_name').val() || ''
349 if ( inputs.display_firstname && inputs.display_lastname ) {
350 inputs.display_firstlast = inputs.display_firstname + ' ' + inputs.display_lastname;
351 inputs.display_lastfirst = inputs.display_lastname + ' ' + inputs.display_firstname;
354 $.each( $('option', select), function( i, el ){
355 dub.push( el.value );
358 $.each(inputs, function( id, value ) {
363 var val = value.replace(/<\/?[a-z][^>]*>/gi, '');
365 if ( inputs[id].length && $.inArray( val, dub ) === -1 ) {
369 }).appendTo( select );
375 $colorpicker = $( '#color-picker' );
376 $stylesheet = $( '#colors-css' );
377 user_id = $( 'input#user_id' ).val();
378 current_user_id = $( 'input[name="checkuser_id"]' ).val();
380 $colorpicker.on( 'click.colorpicker', '.color-option', function() {
384 if ( $this.hasClass( 'selected' ) ) {
388 $this.siblings( '.selected' ).removeClass( 'selected' );
389 $this.addClass( 'selected' ).find( 'input[type="radio"]' ).prop( 'checked', true );
392 if ( user_id === current_user_id ) {
393 // Load the colors stylesheet.
394 // The default color scheme won't have one, so we'll need to create an element.
395 if ( 0 === $stylesheet.length ) {
396 $stylesheet = $( '<link rel="stylesheet" />' ).appendTo( 'head' );
398 $stylesheet.attr( 'href', $this.children( '.css_url' ).val() );
401 if ( typeof wp !== 'undefined' && wp.svgPainter ) {
403 colors = $.parseJSON( $this.children( '.icon_colors' ).val() );
407 wp.svgPainter.setColors( colors );
408 wp.svgPainter.paint();
412 // update user option
414 action: 'save-user-color-scheme',
415 color_scheme: $this.children( 'input[name="admin_color"]' ).val(),
416 nonce: $('#color-nonce').val()
417 }).done( function( response ) {
418 if ( response.success ) {
419 $( 'body' ).removeClass( response.data.previousScheme ).addClass( response.data.currentScheme );
428 $( '#destroy-sessions' ).on( 'click', function( e ) {
431 wp.ajax.post( 'destroy-sessions', {
432 nonce: $( '#_wpnonce' ).val(),
433 user_id: $( '#user_id' ).val()
434 }).done( function( response ) {
435 $this.prop( 'disabled', true );
436 $this.siblings( '.notice' ).remove();
437 $this.before( '<div class="notice notice-success inline"><p>' + response.message + '</p></div>' );
438 }).fail( function( response ) {
439 $this.siblings( '.notice' ).remove();
440 $this.before( '<div class="notice notice-error inline"><p>' + response.message + '</p></div>' );
446 window.generatePassword = generatePassword;
448 /* Warn the user if password was generated but not saved */
449 $( window ).on( 'beforeunload', function () {
450 if ( true === updateLock ) {
451 return userProfileL10n.warn;