Prompt the user before leaving the Customizer if they have unsaved changes. props westonruter. fixes #25439.

git-svn-id: https://develop.svn.wordpress.org/trunk@29025 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Helen Hou-Sandi 2014-07-08 17:03:48 +00:00
parent 22147bfbfb
commit 0298f71032
4 changed files with 95 additions and 39 deletions

View File

@ -915,7 +915,9 @@
var previewer, parent, topFocus,
body = $( document.body ),
overlay = body.children('.wp-full-overlay');
overlay = body.children( '.wp-full-overlay' ),
backBtn = $( '.back' ),
saveBtn = $( '#save' );
// Prevent the form from saving when enter is pressed on an input or select element.
$('#customize-controls').on( 'keydown', function( e ) {
@ -1040,20 +1042,17 @@
processing = state.create( 'processing' );
state.bind( 'change', function() {
var save = $('#save'),
back = $('.back');
if ( ! activated() ) {
save.val( api.l10n.activate ).prop( 'disabled', false );
back.text( api.l10n.cancel );
saveBtn.val( api.l10n.activate ).prop( 'disabled', false );
backBtn.text( api.l10n.cancel );
} else if ( saved() ) {
save.val( api.l10n.saved ).prop( 'disabled', true );
back.text( api.l10n.close );
saveBtn.val( api.l10n.saved ).prop( 'disabled', true );
backBtn.text( api.l10n.close );
} else {
save.val( api.l10n.save ).prop( 'disabled', false );
back.text( api.l10n.cancel );
saveBtn.val( api.l10n.save ).prop( 'disabled', false );
backBtn.text( api.l10n.cancel );
}
});
@ -1081,7 +1080,7 @@
}());
// Button bindings.
$('#save').click( function( event ) {
saveBtn.click( function( event ) {
previewer.save();
event.preventDefault();
}).keydown( function( event ) {
@ -1092,7 +1091,7 @@
event.preventDefault();
});
$('.back').keydown( function( event ) {
backBtn.keydown( function( event ) {
if ( 9 === event.which ) // tab
return;
if ( 13 === event.which ) // enter
@ -1122,16 +1121,25 @@
// If we receive a 'back' event, we're inside an iframe.
// Send any clicks to the 'Return' link to the parent page.
parent.bind( 'back', function() {
$('.back').on( 'click.back', function( event ) {
backBtn.on( 'click.back', function( event ) {
event.preventDefault();
parent.send( 'close' );
});
});
// Prompt user with AYS dialog if leaving the Customizer with unsaved changes
$( window ).on( 'beforeunload', function () {
if ( ! api.state( 'saved' )() ) {
return api.l10n.saveAlert;
}
} );
// Pass events through to the parent.
api.bind( 'saved', function() {
parent.send( 'saved' );
});
$.each( [ 'saved', 'change' ], function ( i, event ) {
api.bind( event, function() {
parent.send( event );
});
} );
// When activated, let the loader handle redirecting the page.
// If no loader exists, redirect the page ourselves (if a url exists).
@ -1198,7 +1206,7 @@
api.trigger( 'ready' );
// Make sure left column gets focus
topFocus = $('.back');
topFocus = backBtn;
topFocus.focus();
setTimeout(function () {
topFocus.focus();

View File

@ -1,4 +1,4 @@
/* global _wpCustomizeLoaderSettings */
/* global _wpCustomizeLoaderSettings, confirm */
window.wp = window.wp || {};
(function( exports, $ ){
@ -36,8 +36,9 @@ window.wp = window.wp || {};
});
// Add navigation listeners.
if ( $.support.history )
if ( $.support.history ) {
this.window.on( 'popstate', Loader.popstate );
}
if ( $.support.hashchange ) {
this.window.on( 'hashchange', Loader.hashchange );
@ -47,35 +48,48 @@ window.wp = window.wp || {};
popstate: function( e ) {
var state = e.originalEvent.state;
if ( state && state.customize )
if ( state && state.customize ) {
Loader.open( state.customize );
else if ( Loader.active )
} else if ( Loader.active ) {
Loader.close();
}
},
hashchange: function() {
var hash = window.location.toString().split('#')[1];
if ( hash && 0 === hash.indexOf( 'wp_customize=on' ) )
if ( hash && 0 === hash.indexOf( 'wp_customize=on' ) ) {
Loader.open( Loader.settings.url + '?' + hash );
}
if ( ! hash && ! $.support.history )
if ( ! hash && ! $.support.history ){
Loader.close();
}
},
beforeunload: function () {
if ( ! Loader.saved() ) {
return Loader.settings.l10n.saveAlert;
}
},
open: function( src ) {
var hash;
if ( this.active )
if ( this.active ) {
return;
}
// Load the full page on mobile devices.
if ( Loader.settings.browser.mobile )
if ( Loader.settings.browser.mobile ) {
return window.location = src;
}
this.active = true;
this.body.addClass('customize-loading');
// Dirty state of customizer in iframe
this.saved = new api.Value( true );
this.iframe = $( '<iframe />', { src: src }).appendTo( this.element );
this.iframe.one( 'load', this.loaded );
@ -92,44 +106,72 @@ window.wp = window.wp || {};
});
this.messenger.bind( 'close', function() {
if ( $.support.history )
if ( $.support.history ) {
history.back();
else if ( $.support.hashchange )
} else if ( $.support.hashchange ) {
window.location.hash = '';
else
} else {
Loader.close();
});
}
} );
// Prompt AYS dialog when navigating away
$( window ).on( 'beforeunload', this.beforeunload );
this.messenger.bind( 'activated', function( location ) {
if ( location )
if ( location ) {
window.location = location;
}
});
hash = src.split('?')[1];
this.messenger.bind( 'saved', function () {
Loader.saved( true );
} );
this.messenger.bind( 'change', function () {
Loader.saved( false );
} );
// Ensure we don't call pushState if the user hit the forward button.
if ( $.support.history && window.location.href !== src )
history.pushState( { customize: src }, '', src );
else if ( ! $.support.history && $.support.hashchange && hash )
window.location.hash = 'wp_customize=on&' + hash;
this.pushState( src );
this.trigger( 'open' );
},
pushState: function ( src ) {
var hash;
// Ensure we don't call pushState if the user hit the forward button.
if ( $.support.history && window.location.href !== src ) {
history.pushState( { customize: src }, '', src );
} else if ( ! $.support.history && $.support.hashchange && hash ) {
hash = src.split( '?' )[1];
window.location.hash = 'wp_customize=on&' + hash;
}
},
opened: function() {
Loader.body.addClass( 'customize-active full-overlay-active' );
},
close: function() {
if ( ! this.active )
if ( ! this.active ) {
return;
}
// Display AYS dialog if customizer is dirty
if ( ! this.saved() && ! confirm( Loader.settings.l10n.saveAlert ) ) {
// Go forward since Customizer is exited by history.back()
history.forward();
return;
}
this.active = false;
this.trigger( 'close' );
// Return focus to link that was originally clicked.
if ( this.link )
if ( this.link ) {
this.link.focus();
}
},
closed: function() {
@ -137,7 +179,9 @@ window.wp = window.wp || {};
Loader.messenger.destroy();
Loader.iframe = null;
Loader.messenger = null;
Loader.saved = null;
Loader.body.removeClass( 'customize-active full-overlay-active' ).removeClass( 'customize-loading' );
$( window ).off( 'beforeunload', Loader.beforeunload );
},
loaded: function() {

View File

@ -382,6 +382,7 @@ function wp_default_scripts( &$scripts ) {
did_action( 'init' ) && $scripts->localize( 'customize-controls', '_wpCustomizeControlsL10n', array(
'activate' => __( 'Save &amp; Activate' ),
'save' => __( 'Save &amp; Publish' ),
'saveAlert' => __( 'The changes you made will be lost if you navigate away from this page.' ),
'saved' => __( 'Saved' ),
'cancel' => __( 'Cancel' ),
'close' => __( 'Close' ),

View File

@ -1874,6 +1874,9 @@ function _wp_customize_loader_settings() {
'url' => esc_url( admin_url( 'customize.php' ) ),
'isCrossDomain' => $cross_domain,
'browser' => $browser,
'l10n' => array(
'saveAlert' => __( 'The changes you made will be lost if you navigate away from this page.' ),
),
);
$script = 'var _wpCustomizeLoaderSettings = ' . json_encode( $settings ) . ';';