BUGFIX Updated jquery.changetracker behaviour in LeftAndMain javascript to properly respond to window.onbeforeunload events

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@92688 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2009-11-21 03:14:14 +00:00
parent 55ed72d2aa
commit f267dac02c
3 changed files with 66 additions and 41 deletions

View File

@ -22,42 +22,47 @@
ChangeTrackerOptions: {},
onmatch: function() {
var self = this;
this._setupChangeTracker();
// Can't bind this through jQuery
window.onbeforeunload = function(e) {return self._checkChangeTracker(false);};
$._super();
},
_setupChangeTracker: function() {
var self = this;
// Don't bind any events here, as we dont replace the
// full <form> tag by any ajax updates they won't automatically reapply
this.changetracker(this.ChangeTrackerOptions());
var autoSaveOnUnload = function(e) {
// @todo TinyMCE coupling
if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();
if(self.is('.changed') && confirm(ss.i18n._t('LeftAndMain.CONFIRMUNSAVED'))) {
// unloads can't be prevented, but we can delay it with a synchronous ajax request
self.ajaxSubmit(
self.find(':submit[name=action_save]'),
null,
{async: false}
);
}
};
// use custom IE 'onbeforeunload' event, as it destroys the DOM
// before going into 'unload'
if(typeof window.onbeforeunload != 'undefined') {
window.onbeforeunload = autoSaveOnUnload;
} else {
$(window).unload(autoSaveOnUnload);
},
/**
* Checks the jquery.changetracker plugin status for this form.
* Usually bound to window.onbeforeunload.
*
* @param {boolean} doConfirm
* @return Either a string with a confirmation message, or the result of a confirm() dialog,
* based on the doConfirm parameter.
*/
_checkChangeTracker: function(doConfirm) {
var self = this;
// @todo TinyMCE coupling
if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();
if(self.is('.changed')) {
var msg = ss.i18n._t('LeftAndMain.CONFIRMUNSAVED');
// returned string will trigger a confirm() dialog,
// but only if the method is triggered by an event
return (doConfirm) ? confirm(msg) : msg;
}
},
/**
* Suppress submission unless it is handled through ajaxSubmit()
* Suppress submission unless it is handled through ajaxSubmit().
*
* @param {Event} e
*/
onsubmit: function(e) {
return false;
@ -66,15 +71,17 @@
/**
* @param {DOMElement} button The pressed button (optional)
* @param {Function} callback Called in complete() handler of jQuery.ajax()
* @param {Object} ajaxOptions Object literal to merge into $.ajax() call
* @param {boolean} loadResponse Render response through _loadResponse() (Default: true)
*/
ajaxSubmit: function(button, callback, ajaxOptions) {
ajaxSubmit: function(button, callback, ajaxOptions, loadResponse) {
var self = this;
// look for save button
if(!button) button = this.find('.Actions :submit[name=action_save]');
// default to first button if none given - simulates browser behaviour
if(!button) button = this.find('.Actions :submit:first');
var self = this;
this.trigger('ajaxsubmit', {button: button});
// set button to "submitting" state
@ -97,17 +104,22 @@
var formData = this.serializeArray();
// add button action
formData.push({name: $(button).attr('name'), value:'1'});
$.ajax($.extend({
jQuery.ajax(jQuery.extend({
url: this.attr('action'),
data: formData,
type: 'POST',
complete: function(xmlhttp, status) {
$(button).removeClass('loading');
// TODO This should be using the plugin API
self.removeClass('changed');
if(callback) callback(xmlhttp, status);
// pass along original form data to enable old/new comparisons
self._loadResponse(xmlhttp.responseText, status, xmlhttp, formData);
if(loadResponse !== false) {
self._loadResponse(xmlhttp.responseText, status, xmlhttp, formData);
}
},
dataType: 'html'
}, ajaxOptions));
@ -131,19 +143,28 @@
},
/**
* @param String url
* @param Function callback (Optional)
* @param {String} url
* @param {Function} callback (Optional)
* @param {ajaxOptions} Object literal merged into the jQuery.ajax() call (Optional)
*/
load: function(url, callback) {
var self = this;
$.ajax({
load: function(url, callback, ajaxOptions) {
var self = this;
// Alert when unsaved changes are present
if(!this._checkChangeTracker(true)) return false;
return jQuery.ajax(jQuery.extend({
url: url,
complete: function(xmlhttp, status) {
self._loadResponse(xmlhttp.responseText, status, xmlhttp);
// TODO This should be using the plugin API
self.removeClass('changed');
if(callback) callback.apply(self, arguments);
self._loadResponse(xmlhttp.responseText, status, xmlhttp);
},
dataType: 'html'
});
}, ajaxOptions));
},
/**
@ -206,6 +227,8 @@
} else {
this.removeForm();
}
this._setupChangeTracker();
// Optionally get the form attributes from embedded fields, see Form->formHtmlContent()
for(var overrideAttr in {'action':true,'method':true,'enctype':true,'name':true}) {
@ -245,7 +268,8 @@
$('#Form_EditForm .Actions :submit').concrete('ss', function($){
return/** @lends ss.Form_EditForm.Actions.submit */{
onmatch: function() {
var self = this;
var self = this;
// TODO Fix once concrete library is updated
this.bind('click', function(e) {return self.clickFake(e);});
},

View File

@ -202,7 +202,7 @@ TreeNodeAPI.prototype = {
if(this.getElementsByTagName('a')[0].href) {
_AJAX_LOADING = true;
if($('sitetree').notify('SelectionChanged', this)) {
jQuery('#Form_EditForm').concrete('ss').ajaxSubmit(null, this.getPageFromServer.bind(this));
this.getPageFromServer();
}
}
},
@ -210,9 +210,7 @@ TreeNodeAPI.prototype = {
getPageFromServer : function() {
var self = this;
this.addNodeClass('loading');
jQuery('#Form_EditForm').concrete('ss').load(
var xmlhttp = jQuery('#Form_EditForm').concrete('ss').load(
jQuery(this).find('a').attr('href'),
function(response) {
self.removeNodeClass('loading');
@ -223,6 +221,9 @@ TreeNodeAPI.prototype = {
}
}
);
if(xmlhttp) this.addNodeClass('loading');
_AJAX_LOADING = false;
},
ajaxExpansion : function() {

View File

@ -24,6 +24,6 @@ if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
'ModelAdmin.DELETED': "Deleted",
'ModelAdmin.VALIDATIONERROR': "Validation Error",
'LeftAndMain.PAGEWASDELETED': "This page was deleted. To edit a page, select it from the left.",
'LeftAndMain.CONFIRMUNSAVED': "You have unsaved changes. Are you sure you want to navigate away from this page?\n\nPress OK to save your changes automatically."
'LeftAndMain.CONFIRMUNSAVED': "Are you sure you want to navigate away from this page?\n\nWARNING: Your changes have not been saved.\n\nPress OK to continue, or Cancel to stay on the current page."
});
}