API CHANGE Forms in ajax responses from LeftAndMain and subclasses include <form> tags, and replace the form itself on the client (rather than just everything inside the form). Form submissions and loading is handled by its container ('.cms-content') to avoid problems with a DOM element replacing itself upon form submission.

This commit is contained in:
Ingo Schommer 2011-06-08 11:35:56 +12:00
parent 0e0a59df99
commit ff54044206
13 changed files with 308 additions and 306 deletions

View File

@ -377,17 +377,20 @@ class LeftAndMain extends Controller {
if(!$title) $title = preg_replace('/Admin$/', '', $class);
return $title;
}
public function show($request) {
// TODO Necessary for TableListField URLs to work properly
if($request->param('ID')) $this->setCurrentPageID($request->param('ID'));
if($this->isAjax()) {
SSViewer::setOption('rewriteHashlinks', false);
$form = $this->getEditForm($request->param('ID'));
$content = $form->formHtmlContent();
if($request->getVar('cms-view-form')) {
$form = $this->getEditForm();
$content = $form->forTemplate();
} else {
// Rendering is handled by template, which will call EditForm() eventually
$content = $this->renderWith($this->getTemplatesWithSuffix('_Content'));
}
} else {
// Rendering is handled by template, which will call EditForm() eventually
$content = $this->renderWith($this->getViewer('show'));
}
@ -636,7 +639,7 @@ class LeftAndMain extends Controller {
// write process might've changed the record, so we reload before returning
$form = $this->getEditForm($record->ID);
return $form->formHtmlContent();
return $form->forTemplate();
}
public function delete($data, $form) {
@ -648,7 +651,7 @@ class LeftAndMain extends Controller {
$record->delete();
if($this->isAjax()) {
return $this->EmptyForm()->formHtmlContent();
return $this->EmptyForm()->forTemplate();
} else {
$this->redirectBack();
}
@ -954,7 +957,7 @@ class LeftAndMain extends Controller {
return $record->ID;
} else if($this->isAjax()) {
$form = $this->getEditForm($record->ID);
return $form->formHtmlContent();
return $form->forTemplate();
} else {
return $this->redirect(Controller::join_links($this->Link('show'), $record->ID));
}

View File

@ -664,7 +664,7 @@ class ModelAdmin_CollectionController extends Controller {
$msg = _t('ModelAdmin.NORESULTS',"Your search didn't return any matching items");
}
return new SS_HTTPResponse(
$resultsForm->formHtmlContent(),
$resultsForm->forTemplate(),
200,
$msg
);
@ -794,7 +794,7 @@ class ModelAdmin_CollectionController extends Controller {
*/
function add($request) {
return new SS_HTTPResponse(
$this->AddForm()->formHtmlContent(),
$this->AddForm()->forTemplate(),
200,
sprintf(
_t('ModelAdmin.ADDFORM', "Fill out this form to add a %s to the database."),
@ -845,6 +845,7 @@ class ModelAdmin_CollectionController extends Controller {
$form = new Form($this, "AddForm", $fields, $actions, $validator);
$form->loadDataFrom($newRecord);
$form->addExtraClass('cms-edit-form');
return $form;
}
@ -862,7 +863,7 @@ class ModelAdmin_CollectionController extends Controller {
$class = $this->parentController->getRecordControllerClass($this->getModelClass());
$recordController = new $class($this, $request, $model->ID);
return new SS_HTTPResponse(
$recordController->EditForm()->formHtmlContent(),
$recordController->EditForm()->forTemplate(),
200,
sprintf(
_t('ModelAdmin.LOADEDFOREDITING', "Loaded '%s' for editing."),
@ -912,7 +913,7 @@ class ModelAdmin_RecordController extends Controller {
function edit($request) {
if ($this->currentRecord) {
if($this->isAjax()) {
$this->response->setBody($this->EditForm()->formHtmlContent());
$this->response->setBody($this->EditForm()->forTemplate());
$this->response->setStatusCode(
200,
sprintf(
@ -924,10 +925,10 @@ class ModelAdmin_RecordController extends Controller {
} else {
// This is really quite ugly; to fix will require a change in the way that customise() works. :-(
return $this->parentController->parentController->customise(array(
'Right' => $this->parentController->parentController->customise(array(
'Content' => $this->parentController->parentController->customise(array(
'EditForm' => $this->EditForm()
))->renderWith(array("{$this->class}_right",'LeftAndMain_right'))
))->renderWith(array('ModelAdmin','LeftAndMain'));
))->renderWith(array("{$this->class}_Content",'ModelAdmin_Content', 'LeftAndMain_Content'))
))->renderWith(array('ModelAdmin', 'LeftAndMain'));
}
} else {
return _t('ModelAdmin.ITEMNOTFOUND', "I can't find that item");
@ -963,6 +964,7 @@ class ModelAdmin_RecordController extends Controller {
$form = new Form($this, "EditForm", $fields, $actions, $validator);
$form->loadDataFrom($this->currentRecord);
$form->addExtraClass('cms-edit-form');
return $form;
}
@ -1017,7 +1019,7 @@ class ModelAdmin_RecordController extends Controller {
function view($request) {
if($this->currentRecord) {
$form = $this->ViewForm();
return $form->formHtmlContent();
return $form->forTemplate();
} else {
return _t('ModelAdmin.ITEMNOTFOUND');
}

View File

@ -27,8 +27,8 @@ html, body { width: 100%; height: 100%; padding: 0; margin: 0; overflow: hidden;
.cms-menu .cms-panel-content { width: 250px; }
.cms-menu.collapsed { width: 40px; }
.cms-preview, .cms-menu, .cms-content, .cms-content-header, .cms-content-tools, .cms-content-form { display: -moz-inline-box; -moz-box-orient: vertical; display: inline-block; vertical-align: middle; *vertical-align: auto; }
.cms-preview, .cms-menu, .cms-content, .cms-content-header, .cms-content-tools, .cms-content-form { *display: inline; }
.cms-preview, .cms-menu, .cms-content, .cms-content-header, .cms-content-tools, .cms-content-form, .cms-edit-form { display: -moz-inline-box; -moz-box-orient: vertical; display: inline-block; vertical-align: middle; *vertical-align: auto; }
.cms-preview, .cms-menu, .cms-content, .cms-content-header, .cms-content-tools, .cms-content-form, .cms-edit-form { *display: inline; }
.cms-content-tools { width: 230px; overflow: auto; }
.cms-content-tools .cms-panel-header, .cms-content-tools .cms-panel-content { padding: 10px; }

View File

@ -144,7 +144,7 @@ li.jstree-closed > ul { display: none; }
.cms-menu-list li { background-color: #b0bfc6; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #b0bfc6), color-stop(100%, #758f9b)); background-image: -moz-linear-gradient(top, #b0bfc6 0%, #758f9b 100%); background-image: linear-gradient(top, #b0bfc6 0%, #758f9b 100%); border-bottom: 1px solid #aaaaaa; }
.cms-menu-list li a { display: block; height: 32px; vertical-align: middle; font-size: 14px; text-shadow: #aaaaaa 1px 1px 1px; color: #333333; padding: 5px; }
.cms-menu-list li a .icon { display: block; float: left; margin-right: 5px; background: url('../images/icons-32.png?1305837920') no-repeat; width: 32px; height: 32px; overflow: hidden; background-position: 0px 0px; }
.cms-menu-list li a .icon { display: block; float: left; margin-right: 5px; background: url('../images/icons-32.png?1306441269') no-repeat; width: 32px; height: 32px; overflow: hidden; background-position: 0px 0px; }
.cms-menu-list li a:hover .icon { background-position: -32px 0px; }
.cms-menu-list li a .text { display: block; padding-top: 10px; }
.cms-menu-list li.current { background-color: #338dc1; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #338dc1), color-stop(100%, #1e5270)); background-image: -moz-linear-gradient(top, #338dc1 0%, #1e5270 100%); background-image: linear-gradient(top, #338dc1 0%, #1e5270 100%); }

View File

@ -91,8 +91,9 @@
data.push({name:button.attr('name'),value:button.val()});
// TODO Should be set by hiddenfield already
jQuery('.cms-edit-form').entwine('ss').loadForm(
jQuery('.cms-content').entwine('ss').loadForm(
this.attr('action'),
null,
function() {
// Tree updates are triggered by Form_EditForm load events
button.removeClass('loading');
@ -129,7 +130,7 @@
dropdown.find('option').remove();
//Use tree hints to find allowed children for this node
if (className && typeof siteTreeHints !== 'undefined') {
if (className && siteTreeHints) {
disallowed = siteTreeHints[className].disallowedChildren;
}

View File

@ -4,36 +4,228 @@
$('.LeftAndMain .cms-content').entwine({
redraw: function() {
onmatch: function() {
var self = this;
// Listen to tree selection events
$('.cms-tree').bind('select_node.jstree', function(e, data) {
var node = data.rslt.obj, loadedNodeID = self.find(':input[name=ID]').val()
// Don't allow checking disabled nodes
if($(node).hasClass('disabled')) return false;
// Don't allow reloading of currently selected node,
// mainly to avoid doing an ajax request on initial page load
if($(node).data('id') == loadedNodeID) return;
var url = $(node).find('a:first').attr('href');
if(url && url != '#') {
var xmlhttp = self.loadForm(
url,
null,
function(response) {}
);
} else {
self.removeForm();
}
});
this._super();
},
beforeLoad: function(url) {
this.addClass('loading');
this.cleanup();
},
cleanup: function() {
this.empty();
},
loadPanel: function(url, callback, ajaxOptions) {
/**
* Function: loadForm
*
* Parameters:
* (String) url - ..
* (Function) callback - (Optional) Called after the form content as been loaded
* (Object) ajaxOptions - Object literal merged into the jQuery.ajax() call (Optional)
*
* Returns:
* (XMLHTTPRequest)
*/
loadForm: function(url, form, callback, ajaxOptions) {
var self = this;
if(!form || !form.length) var form = $('.cms-content-form form:first');
this.trigger('load', {url: url, args: arguments});
// Alert when unsaved changes are present
if(form._checkChangeTracker(true) == false) return false;
// hide existing form - shown again through _loadResponse()
form.addClass('loading');
this.cleanup();
this.trigger('loadform', {form: form, url: url});
form.cleanup();
// TODO Add browser history support
return $.ajax($.extend({
return jQuery.ajax(jQuery.extend({
url: url,
// Ensure that form view is loaded (rather than whole "Content" template)
data: {'cms-view-form': 1},
complete: function(xmlhttp, status) {
self.loadPanel_onSuccess(xmlhttp.responseText, status, xmlhttp);
self.removeClass('loading');
self.loadForm_responseHandler(form, xmlhttp.responseText, status, xmlhttp);
if(callback) callback.apply(self, arguments);
},
dataType: 'html'
}, ajaxOptions));
},
loadForm_responseHandler: function(oldForm, html, status, xmlhttp) {
oldForm.replaceWith(html); // triggers onmatch() on form
// set status message based on response
var _statusMessage = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.statusText;
},
/**
* Function: ajaxSubmit
*
* Parameters:
* {DOMElement} button - The pressed button (optional)
* {Function} callback - Called in complete() handler of jQuery.ajax()
* {Object} ajaxOptions - Object literal to merge into $.ajax() call
* {boolean} loadResponse - Render response through _loadResponse() (Default: true)
*
* Returns:
* (boolean)
*/
submitForm: function(form, 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');
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
statusMessage("Validation failed.", "bad");
loadPanel_onSuccess: function(html, status, xmlhttp) {
this.html(html);
this.redraw();
$(button).removeClass('loading');
return false;
}
// save tab selections in order to reconstruct them later
var selectedTabs = [];
form.find('.ss-tabset').each(function(i, el) {
if($(el).attr('id')) selectedTabs.push({id:$(el).attr('id'), selected:$(el).tabs('option', 'selected')});
});
// get all data from the form
var formData = form.serializeArray();
// add button action
formData.push({name: $(button).attr('name'), value:'1'});
jQuery.ajax(jQuery.extend({
url: form.attr('action'),
data: formData,
type: 'POST',
complete: function(xmlhttp, status) {
$(button).removeClass('loading');
// TODO This should be using the plugin API
form.removeClass('changed');
if(callback) callback(xmlhttp, status);
// pass along original form data to enable old/new comparisons
if(loadResponse !== false) {
self.submitForm_responseHandler(form, xmlhttp.responseText, status, xmlhttp, formData);
}
// Re-init tabs (in case the form tag itself is a tabset)
if(self.hasClass('ss-tabset')) self.removeClass('ss-tabset').addClass('ss-tabset');
// re-select previously saved tabs
$.each(selectedTabs, function(i, selectedTab) {
form.find('#' + selectedTab.id).tabs('select', selectedTab.selected);
});
},
dataType: 'html'
}, ajaxOptions));
return false;
},
/**
* Function: _loadResponse
*
* Parameters:
* {String} data - Either HTML for straight insertion, or eval'ed JavaScript.
* If passed as HTML, it is assumed that everying inside the <form> tag is replaced,
* but the old <form> tag itself stays intact.
* {String} status
* {XMLHTTPRequest} xmlhttp - ..
* {Array} origData - The original submitted data, useful to do comparisons of changed
* values in new form output, e.g. to detect a URLSegment being changed on the serverside.
* Array in jQuery serializeArray() notation.
*/
submitForm_responseHandler: function(oldForm, data, status, xmlhttp, origData) {
if(status == 'success') {
var form = this.replaceForm(oldForm, data);
Behaviour.apply(); // refreshes ComplexTableField
this.trigger('loadnewpage', {form: form, origData: origData, xmlhttp: xmlhttp});
}
// set status message based on response
var _statusMessage = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.statusText;
},
/**
* @return {jQuery} New form element
*/
replaceForm: function(form, html) {
form.cleanup();
if(html) {
var parent = form.parent(), id = form.attr('id');
form.replaceWith(html);
// Try to get the new form by ID (assuming they're identical), otherwise fall back to the first form in the parent
return id ? $('#' + id) : parent.children('form:first');
} else {
this.removeForm(form);
return null;
}
},
/**
* Function: removeForm
*
* Remove everying inside the <form> tag
* with a custom HTML fragment. Useful e.g. for deleting a page in the CMS.
* Checks for unsaved changes before removing the form
*
* Parameters:
* {String} placeholderHtml - Short note why the form has been removed, displayed in <p> tags.
* Falls back to the default RemoveText() option (Optional)
*/
removeForm: function(form, placeholderHtml) {
if(!placeholderHtml) placeholderHtml = this.getPlaceholderHtml();
// Alert when unsaved changes are present
if(form._checkChangeTracker(true) == false) return;
this.trigger('removeform');
this.html(placeholderHtml);
// TODO This should be using the plugin API
this.removeClass('changed');
}
});
});

View File

@ -40,33 +40,47 @@
var self = this;
this._setupChangeTracker();
$('.cms-tree').bind('select_node.jstree', function(e, data) {
var node = data.rslt.obj, loadedNodeID = self.find(':input[name=ID]').val()
// Don't allow checking disabled nodes
if($(node).hasClass('disabled')) return false;
// Don't allow reloading of currently selected node,
// mainly to avoid doing an ajax request on initial page load
if($(node).data('id') == loadedNodeID) return;
var url = $(node).find('a:first').attr('href');
if(url && url != '#') {
var xmlhttp = self.loadForm(
url,
function(response) {}
);
} else {
self.removeForm();
}
});
// Can't bind this through jQuery
window.onbeforeunload = function(e) {return self._checkChangeTracker(false);};
// focus input on first form element
this.find(':input:visible:first').focus();
// Optionally get the form attributes from embedded fields, see Form->formHtmlContent()
for(var overrideAttr in {'action':true,'method':true,'enctype':true,'name':true}) {
var el = this.find(':input[name='+ '_form_' + overrideAttr + ']');
if(el) {
this.attr(overrideAttr, el.val());
el.remove();
}
}
// TODO
// // Rewrite # links
// html = html.replace(/(<a[^>]+href *= *")#/g, '$1' + window.location.href.replace(/#.*$/,'') + '#');
//
// // Rewrite iframe links (for IE)
// html = html.replace(/(<iframe[^>]*src=")([^"]+)("[^>]*>)/g, '$1' + $('base').attr('href') + '$2$3');
// Show validation errors if necessary
if(this.hasClass('validationerror')) {
// TODO validation shouldnt need a special case
statusMessage(ss.i18n._t('ModelAdmin.VALIDATIONERROR', 'Validation Error'), 'bad');
}
this._super();
},
onunmatch: function() {
// Prepare iframes for removal, otherwise we get loading bugs
this.find('iframe').each(function() {
this.contentWindow.location.href = 'about:blank';
$(this).remove();
});
this._super();
},
/**
* Function: _setupChangeTracker
@ -114,90 +128,11 @@
* Suppress submission unless it is handled through ajaxSubmit().
*/
onsubmit: function(e) {
this.ajaxSubmit();
this.parents('.cms-content').submitForm(this);
return false;
},
/**
* Function: ajaxSubmit
*
* Parameters:
* {DOMElement} button - The pressed button (optional)
* {Function} callback - Called in complete() handler of jQuery.ajax()
* {Object} ajaxOptions - Object literal to merge into $.ajax() call
* {boolean} loadResponse - Render response through _loadResponse() (Default: true)
*
* Returns:
* (boolean)
*/
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');
this.trigger('ajaxsubmit', {button: button});
// set button to "submitting" state
$(button).addClass('loading');
// @todo TinyMCE coupling
if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();
// validate if required
if(!this.validate()) {
// TODO Automatically switch to the tab/position of the first error
statusMessage("Validation failed.", "bad");
$(button).removeClass('loading');
return false;
}
// save tab selections in order to reconstruct them later
var selectedTabs = [];
this.find('.ss-tabset').each(function(i, el) {
if($(el).attr('id')) selectedTabs.push({id:$(el).attr('id'), selected:$(el).tabs('option', 'selected')});
});
// get all data from the form
var formData = this.serializeArray();
// add button action
formData.push({name: $(button).attr('name'), value:'1'});
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
if(loadResponse !== false) {
self._loadResponse(xmlhttp.responseText, status, xmlhttp, formData);
}
// Re-init tabs (in case the form tag itself is a tabset)
if(self.hasClass('ss-tabset')) self.removeClass('ss-tabset').addClass('ss-tabset');
// re-select previously saved tabs
$.each(selectedTabs, function(i, selectedTab) {
self.find('#' + selectedTab.id).tabs('select', selectedTab.selected);
});
},
dataType: 'html'
}, ajaxOptions));
return false;
},
/**
* Function: validate
*
@ -217,67 +152,6 @@
return isValid;
},
/**
* Function: loadForm
*
* Parameters:
* (String) url - ..
* (Function) callback - (Optional) Called after the form content as been loaded
* (Object) ajaxOptions - Object literal merged into the jQuery.ajax() call (Optional)
*
* Returns:
* (XMLHTTPRequest)
*/
loadForm: function(url, callback, ajaxOptions) {
var self = this;
// Alert when unsaved changes are present
if(this._checkChangeTracker(true) == false) return false;
// hide existing form - shown again through _loadResponse()
this.addClass('loading');
this.trigger('load', {url: url});
this.cleanup();
return jQuery.ajax(jQuery.extend({
url: url,
complete: function(xmlhttp, status) {
// TODO This should be using the plugin API
self.removeClass('changed');
self._loadResponse(xmlhttp.responseText, status, xmlhttp);
self.removeClass('loading');
if(callback) callback.apply(self, arguments);
},
dataType: 'html'
}, ajaxOptions));
},
/**
* Function: removeForm
*
* Remove everying inside the <form> tag
* with a custom HTML fragment. Useful e.g. for deleting a page in the CMS.
* Checks for unsaved changes before removing the form
*
* Parameters:
* {String} placeholderHtml - Short note why the form has been removed, displayed in <p> tags.
* Falls back to the default RemoveText() option (Optional)
*/
removeForm: function(placeholderHtml) {
if(!placeholderHtml) placeholderHtml = this.getPlaceholderHtml();
// Alert when unsaved changes are present
if(this._checkChangeTracker(true) == false) return;
this.trigger('removeform');
this.html(placeholderHtml);
// TODO This should be using the plugin API
this.removeClass('changed');
},
/**
* Function: cleanup
*
@ -287,79 +161,9 @@
cleanup: function() {
if((typeof tinymce != 'undefined') && tinymce.editors) {
$(tinymce.editors).each(function() {
if(typeof(this.remove) == 'function') {
this.remove();
}
if(typeof(this.remove) == 'function') this.remove();
});
}
},
/**
* Function: _loadResponse
*
* Parameters:
* {String} data - Either HTML for straight insertion, or eval'ed JavaScript.
* If passed as HTML, it is assumed that everying inside the <form> tag is replaced,
* but the old <form> tag itself stays intact.
* {String} status
* {XMLHTTPRequest} xmlhttp - ..
* {Array} origData - The original submitted data, useful to do comparisons of changed
* values in new form output, e.g. to detect a URLSegment being changed on the serverside.
* Array in jQuery serializeArray() notation.
*/
_loadResponse: function(data, status, xmlhttp, origData) {
if(status == 'success') {
this.cleanup();
var html = data;
// Rewrite # links
html = html.replace(/(<a[^>]+href *= *")#/g, '$1' + window.location.href.replace(/#.*$/,'') + '#');
// Rewrite iframe links (for IE)
html = html.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();
});
// update form content
if(html) {
this.html(html);
} else {
this.removeForm();
}
// If the form itself is a tabset, force re-rendering
if(this.hasClass('ss-tabset')) this.tabs('destroy').tabs();
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}) {
var el = this.find(':input[name='+ '_form_' + overrideAttr + ']');
if(el) {
this.attr(overrideAttr, el.val());
el.remove();
}
}
Behaviour.apply(); // refreshes ComplexTableField
// focus input on first form element
this.find(':input:visible:first').focus();
this.trigger('loadnewpage', {data: data, origData: origData, xmlhttp: xmlhttp});
}
// set status message based on response
var _statusMessage = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.statusText;
if(this.hasClass('validationerror')) {
// TODO validation shouldnt need a special case
statusMessage(ss.i18n._t('ModelAdmin.VALIDATIONERROR', 'Validation Error'), 'bad');
}
}
});
@ -376,7 +180,7 @@
* Function: onclick
*/
onclick: function(e) {
jQuery('.cms-edit-form').entwine('ss').ajaxSubmit(this);
$('.cms-content').submitForm(this.parents('form'), this);
return false;
}
});

View File

@ -73,6 +73,8 @@
},
redraw: function() {
// Not all edit forms are layouted
var editForm = this.find('.cms-edit-form[data-layout]').layout();
this.find('.cms-content').layout();
this.find('.cms-container').layout({resize: false})
}

View File

@ -71,8 +71,9 @@
var btn = $(this[0].clickedButton);
btn.addClass('loading');
$('.cms-edit-form').loadForm(
$('.cms-content').loadForm(
this.attr('action'),
null,
function() {
btn.removeClass('loading');
},
@ -135,7 +136,8 @@
onclick: function(e) {
var firstLink = this.find('a[href]');
if(!firstLink) return;
$('.cms-edit-form').loadForm(firstLink.attr('href'));
window.History.pushState({selector: '.cms-edit-form'}, '', firstLink.attr('href'));
return false;
}
});
@ -153,8 +155,9 @@
className = $('select option:selected', this).val();
requestPath = this.attr('action').replace('ManagedModelsSelect', className + '/add');
var $button = $(':submit', this);
$('.cms-edit-form').loadForm(
$('.cms-content').loadForm(
requestPath,
null,
function() {
$button.removeClass('loading');
$button = null;

View File

@ -51,7 +51,8 @@ body .ui-widget {
.cms-content,
.cms-content-header,
.cms-content-tools,
.cms-content-form {
.cms-content-form,
.cms-edit-form {
@include inline-block;
}

View File

@ -1,3 +1 @@
<div class="cms-content center ss-tabset" data-layout="{type: 'border'}">
$EditForm
</div>
$EditForm

View File

@ -1,34 +1,32 @@
<div class="cms-content-header north">
<div>
<h2> My Page Title</h2>
<% if Fields.hasTabset %>
<% with Fields.fieldByName('Root') %>
<div class="cms-content-header-tabs">
<ul>
<% control Tabs %>
<li><a href="#$id">$Title</a></li>
<% end_control %>
</ul>
<% end_with %>
</div>
<% end_if %>
<% if IncludeFormTag %>
<form $FormAttributes data-layout="{type: 'border'}">
<% end_if %>
<div class="cms-content-header north">
<div>
<h2> My Page Title</h2>
<% if Fields.hasTabset %>
<% with Fields.fieldByName('Root') %>
<div class="cms-content-header-tabs">
<ul>
<% control Tabs %>
<li><a href="#$id">$Title</a></li>
<% end_control %>
</ul>
<% end_with %>
</div>
<% end_if %>
<!-- <div class="cms-content-search">...</div> -->
<!-- <div class="cms-content-search">...</div> -->
</div>
</div>
</div>
<div class="cms-content-form center">
<div class="cms-content-form center">
<% if IncludeFormTag %>
<form $FormAttributes>
<% end_if %>
<!-- <div class="cms-content-tools west">
$Left
</div> -->
<!-- <div class="cms-content-tools west">
$Left
</div> -->
<div class="center">
<% if Message %>
<p id="{$FormName}_error" class="message $MessageType">$Message</p>
<% else %>
@ -55,8 +53,6 @@
<% end_if %>
</div>
<% if IncludeFormTag %>
</form>
<% end_if %>
</div>
<% if IncludeFormTag %>
</form>
<% end_if %>