BUGFIX Fixed form change tracking in the CMS, integrated it into menu changes. Reduced TinyMCE coupling.

This commit is contained in:
Ingo Schommer 2011-12-15 12:23:32 +01:00
parent a6e2316766
commit 482324b6ae
5 changed files with 42 additions and 39 deletions

View File

@ -70,7 +70,7 @@
}
// Alert when unsaved changes are present
if(form._checkChangeTracker(true) == false) return false;
if(!form.confirmUnsavedChanges()) return false;
// hide existing form - shown again through _loadResponse()
form.addClass('loading');
@ -138,14 +138,12 @@
// default to first button if none given - simulates browser behaviour
if(!button) button = this.find('.Actions :submit:first');
form.trigger('beforesave');
this.trigger('submitform', {form: form, button: button});
// set button to "submitting" state
$(button).addClass('loading');
// @todo TinyMCE coupling
if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();
// validate if required
if(!form.validate()) {
// TODO Automatically switch to the tab/position of the first error
@ -252,7 +250,7 @@
removeForm: function(form, placeholderHtml) {
if(!placeholderHtml) placeholderHtml = this.getPlaceholderHtml();
// Alert when unsaved changes are present
if(form._checkChangeTracker(true) == false) return;
if(!form.confirmUnsavedChanges()) return;
this.trigger('removeform');
this.html(placeholderHtml);
// TODO This should be using the plugin API

View File

@ -113,34 +113,20 @@
},
/**
* Function: _checkChangeTracker
* Function: confirmUnsavedChanges
*
* Checks the jquery.changetracker plugin status for this form.
* Usually bound to window.onbeforeunload.
*
* Parameters:
* {boolean} isUnloadEvent - ..
* Checks the jquery.changetracker plugin status for this form,
* and asks the user for confirmation via a browser dialog if changes are detected.
* Doesn't cancel any unload or form removal events, you'll need to implement this based on the return
* value of this message.
*
* Returns:
* (String) Either a string with a confirmation message, or the result of a confirm() dialog,
* based on the isUnloadEvent parameter.
* (Boolean) FALSE if the user wants to abort with changes present, TRUE if no changes are detected
* or the user wants to discard them.
*/
_checkChangeTracker: function(isUnloadEvent) {
var self = this;
// @todo TinyMCE coupling
if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();
// check for form changes
if(self.is('.changed')) {
// returned string will trigger a confirm() dialog,
// but only if the method is triggered by an event
if(isUnloadEvent) {
return confirm(ss.i18n._t('LeftAndMain.CONFIRMUNSAVED'));
} else {
return ss.i18n._t('LeftAndMain.CONFIRMUNSAVEDSHORT');
}
}
confirmUnsavedChanges: function() {
this.trigger('beforesave');
return (this.is('.changed')) ? confirm(ss.i18n._t('LeftAndMain.CONFIRMUNSAVED')) : true;
},
/**
@ -205,6 +191,13 @@
* Constructor: onmatch
*/
onmatch : function() {
var self = this;
this.closest('form').bind('beforesave', function() {
tinyMCE.triggerSave();
// TinyMCE assigns value attr directly, which doesn't trigger change event
self.trigger('change');
});
// Only works after TinyMCE.init() has been invoked, see $(window).bind() call below for details.
this.redraw();

View File

@ -22,6 +22,9 @@
* </ul>
* </li>
* </ul>
*
* Custom Events:
* - 'select': Fires when a menu item is selected (on any level).
*/
$('.cms-menu-list').entwine({
onmatch: function() {
@ -82,7 +85,7 @@
this.siblings().find('li').removeClass('current');
if(parent) parent.addClass('current').siblings().removeClass('current');
this.getMenu().updateItems();
this.trigger('select');
}
});
@ -108,10 +111,8 @@
// Ignore external links, fallback to standard link behaviour
if(e.which > 1 || this.is(':external')) return;
e.preventDefault();
// Expand this, and collapse all other items
var item = this.getMenuItem();
item.select();
var url = this.attr('href');
if(this.is(':internal')) url = $('base').attr('href') + url;
@ -119,13 +120,13 @@
var children = item.find('li');
if(children.length) {
children.first().find('a').click();
} else if(window.History.enabled) {
// Active menu item is set based on X-Controller ajax header,
// which matches one class on the menu
window.History.pushState({}, '', url);
} else {
window.location = url;
// Load URL, but give the loading logic an opportunity to veto the action
// (e.g. because of unsaved changes)
if(!$('.cms-container').loadPanel(url)) return false;
}
item.select();
}
});

View File

@ -112,7 +112,16 @@
*/
loadPanel: function(url, title, data) {
var data = data || {}, selector = data.selector || '.cms-content', contentEl = $(selector);
// Check change tracking (can't use events as we need a way to cancel the current state change)
var trackedEls = contentEl.find(':data(changetracker)').add(contentEl.filter(':data(changetracker)'));
if(trackedEls.length) {
var abort = false;
trackedEls.each(function() {
if(!$(this).confirmUnsavedChanges()) abort = true;
});
if(abort) return;
}
if(window.History.enabled) {
// Active menu item is set based on X-Controller ajax header,
// which matches one class on the menu

View File

@ -68,6 +68,8 @@
.each(function() {
$(this).data('changetracker.origVal', $(this).val());
});
this.data('changetracker', true);
};
/**