ENHANCEMENT Initial 'concrete' implementation of CMS forms incl. ajax saving, validation placeholders and session-pinging

MINOR Removed 'innerLayout' from CMSMain.js javascript

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@92599 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2009-11-21 02:36:00 +00:00
parent 1b312ae81b
commit df7713f894
2 changed files with 284 additions and 62 deletions

View File

@ -1,10 +1,24 @@
var outerLayout;
var innerLayout;
jQuery(document).ready(function () {
(function($) {
$('body.CMSMain').concrete({ss:{cmsMain:{
mainLayout: null,
onmatch: function() {
this.mainLayout = this.ss().cmsMain()._setupLayout();
},
/**
* Initialize jQuery layout manager with the following panes:
* - east: Tree, Page Version History, Site Reports
* - center: Form
* - west: "Insert Image", "Insert Link", "Insert Flash" panes
* - north: CMS area menu bar
* - south: "Page view", "profile" and "logout" links
*/
_setupLayout: function() {
// layout containing the tree, CMS menu, the main form etc.
outerLayout = jQuery('body').layout({
var layout = $('body').layout({
defaults: {
// TODO Reactivate once we have localized values
togglerTip_open: '',
@ -12,55 +26,43 @@ jQuery(document).ready(function () {
resizerTip: '',
sliderTip: ''
},
// contains CMSMenu
north: {
slidable: false,
resizable: false,
size: 35,
togglerLength_open: 0
},
// "Page view", "profile" and "logout" links
south: {
slidable: false,
resizable: false,
size: 20,
togglerLength_open: 0
},
// "Insert link" etc.
east: {
initClosed: true,
fxName: "none"
},
// Tree, page version history
west: {
size: 250,
onresize: function () { jQuery("#treepanes").accordion("resize"); },
onopen: function () { jQuery("#treepanes").accordion("resize"); },
onresize: function () { $("#treepanes").accordion("resize"); },
onopen: function () { $("#treepanes").accordion("resize"); },
fxName: "none"
},
// Page forms
center: {
onresize: "innerLayout.resizeAll"
}
center: {}
});
// Layout for the form and its buttons
innerLayout = jQuery('#right').layout({
center: {},
south: {
slidable: false,
resizable: false,
size: 30,
togglerLength_open: 0
}
})
// Adjust tree accordion etc. in left panel to work correctly
// with jQuery.layout (see http://layout.jquery-dev.net/tips.html#Widget_Accordion)
jQuery("#treepanes").accordion({
this.find("#treepanes").accordion({
fillSpace: true,
animated: false
});
});
return layout;
}
}}});
$('#Form_EditForm').concrete({
})
})(jQuery);

View File

@ -1,3 +1,225 @@
(function($) {
/**
* Main LeftAndMain interface with some control
* panel and an edit form.
*
* Events:
* - beforeSave
* - afterSave
* - beforeValidate
* - afterValidate
*/
$('.LeftAndMain').concrete({ss:{leftAndMain:{
onmatch: function() {
this.ss().leftAndMain()._setupPinging();
this.ss().leftAndMain()._setupButtons();
},
_setupPinging: function() {
var pingIntervalSeconds = 5*60;
// setup pinging for login expiry
setInterval(function() {
$.get("Security/ping");
}, pingIntervalSeconds * 1000);
},
/**
* Make all buttons "hoverable" with jQuery theming.
*/
_setupButtons: function() {
// Initialize buttons
this.find(':submit, button').livequery(function() {
jQuery(this).addClass(
'ui-state-default ' +
'ui-corner-all'
)
.hover(
function() {
$(this).addClass('ui-state-hover');
},
function() {
$(this).removeClass('ui-state-hover');
}
)
.focus(function() {
$(this).addClass('ui-state-focus');
})
.blur(function() {
$(this).removeClass('ui-state-focus');
});
});
}
}}});
/**
* Base edit form, provides ajaxified saving
* and reloading itself through the ajax return values.
* Takes care of resizing tabsets within the layout container.
*/
$('#Form_EditForm').concrete({ss:{
onmatch: function() {
var $this = this;
// artificially delay the resize event 200ms
// to avoid overlapping height changes in different onresize() methods
$(window).resize(function () {
var timerID = "timerLayout_"+this.id;
if (window[timerID]) clearTimeout(window[timerID]);
window[timerID] = setTimeout(function() {$this.ss()._resizeChildren();}, 200);
}).trigger('resize');
// trigger resize whenever new tabs are shown
// @todo This is called multiple times when tabs are loaded
this.find('.ss-tabset').bind('tabsshow', function() {$this.ss()._resizeChildren();});
},
/**
* Suppress submission unless it is handled through save()
*/
onsubmit: function(e) {
return false;
},
/**
* @param DOMElement button The pressed button (optiona)
*/
ajaxSubmit: function(button) {
// default to first button if none given - simulates browser behaviour
if(!button) button = this.find(':submit:first');
var $form = this;
this.trigger('beforeSubmit', [button]);
// set button to "submitting" state
$(button).addClass('loading');
// @todo TinyMCE coupling
if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();
// validate if required
if(!this.ss().validate()) {
this.trigger('validationError', [button]);
// TODO Automatically switch to the tab/position of the first error
statusMessage("Validation failed.", "bad");
if($('Form_EditForm_action_save') && $('Form_EditForm_action_save').stopLoading) $('Form_EditForm_action_save').stopLoading();
return false;
}
// get all data from the form
var data = this.serializeArray();
// add button action
data.push({name: $(button).attr('name'), value:'1'});
$.post(
this.attr('action'),
data,
function(result) {
$(button).removeClass('loading');
$form.trigger('afterSubmit', [result]);
$form.ss().loadNewPage();
},
// @todo Currently all responses are assumed to be evaluated
'script'
);
return false;
},
/**
* Hook in (optional) validation routines.
* Currently clientside validation is not supported out of the box in the CMS.
*
* @return boolean
*/
validate: function() {
this.trigger('beforeValidate');
var isValid = true;
this.trigger('afterValidate', [isValid]);
return isValid;
},
loadNewPage: function(result) {
// TinyMCE coupling
if(typeof tinymce_removeAll != 'undefined') tinymce_removeAll();
// Rewrite # links
result = result.replace(/(<a[^>]+href *= *")#/g, '$1' + window.location.href.replace(/#.*$/,'') + '#');
// Rewrite iframe links (for IE)
result = result.replace(/(<iframe[^>]*src=")([^"]+)("[^>]*>)/g, '$1' + $('base').attr('href') + '$2$3');
// Prepare iframes for removal, otherwise we get loading bugs
this.find('iframe').each(function() {
this.contentWindow.location.href = 'about:blank';
this.remove();
})
this.html(result);
if(this.hasClass('validationerror')) {
statusMessage(ss.i18n._t('ModelAdmin.VALIDATIONERROR', 'Validation Error'), 'bad');
} else {
statusMessage(ss.i18n._t('ModelAdmin.SAVED', 'Saved'), 'good');
}
Behaviour.apply(); // refreshes ComplexTableField
// If there's a title field and it's got a "new XX" value, focus/select that first
// This is really a little too CMS-specific (as opposed to LeftAndMain), but the cleanup can happen after jQuery refactoring
if($('input#Form_EditForm_Title') && $('input#Form_EditForm_Title').value.match(/^new/i)) {
$('input#Form_EditForm_Title').select();
}
},
/**
* Resize elements in center panel
* to fit the boundary box provided by the layout manager
*/
_resizeChildren: function() {
this.fitHeightToParent();
$('fieldset', this).fitHeightToParent();
// Order of resizing is important: Outer to inner
// TODO Only supports two levels of tabs at the moment
$('fieldset > .ss-tabset', this).fitHeightToParent();
$('fieldset > .ss-tabset > .tab', this).fitHeightToParent();
$('fieldset > .ss-tabset > .tab > .ss-tabset', this).fitHeightToParent();
$('fieldset > .ss-tabset > .tab > .ss-tabset > .tab', this).fitHeightToParent();
}
}});
$('#Form_EditForm .Actions :submit').concrete({ss:{
onclick: function(e) {
$(this[0].form).ss().ajaxSubmit(this);
return false;
}
}});
})(jQuery);
jQuery(document).ready(function() {
// @todo remove
jQuery.concrete.triggerMatching();
});
var _AJAX_LOADING = false;
Behaviour.register({
@ -442,9 +664,7 @@ function hideIndicator(id) {
Effect.Fade(id, {duration: 0.3});
}
setInterval(function() {
new Ajax.Request("Security/ping");
}, 180*1000);
/**
* Find and enable TinyMCE on all htmleditor fields