From 0298f71032953dc120ce2cef0d058829513d3f3c Mon Sep 17 00:00:00 2001 From: Helen Hou-Sandi Date: Tue, 8 Jul 2014 17:03:48 +0000 Subject: [PATCH] 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 --- src/wp-admin/js/customize-controls.js | 42 +++++++----- src/wp-includes/js/customize-loader.js | 88 +++++++++++++++++++------- src/wp-includes/script-loader.php | 1 + src/wp-includes/theme.php | 3 + 4 files changed, 95 insertions(+), 39 deletions(-) diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js index 4bfc252a44..a550d3e9bc 100644 --- a/src/wp-admin/js/customize-controls.js +++ b/src/wp-admin/js/customize-controls.js @@ -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(); diff --git a/src/wp-includes/js/customize-loader.js b/src/wp-includes/js/customize-loader.js index cccf71acc8..98bb32f1eb 100644 --- a/src/wp-includes/js/customize-loader.js +++ b/src/wp-includes/js/customize-loader.js @@ -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 = $( '