/**
* Client-side code for [Netzke::Form::Base](http://www.rubydoc.info/github/netzke/netzke-basepack/Netzke/Form/Base)
* @class Netzke.Form.Base
*/
{
bodyStyle : 'padding:5px 5px 0',
autoScroll : true,
fieldDefaults : { labelWidth : 150 },
defaults : {
anchor : '-20' // to leave some space for the scrollbar
},
initComponent: function(){
// passing config options to BasicForm is possible via initialConfig only
// see Ext.form.Panel documentation
this.initialConfig = {
// form tracks it's default field values so they can be reset()
trackResetOnLoad: true,
}
// if (!this.bbar && !this.readOnly) this.bbar = {xtype: 'toolbar'}; // an empty bbar by default, so that we can dynamically add buttons
// Custom error reader. We don't use it to process form values, but rather to normalize the response from the server in case of "real" (iframe) form submit.
ErrorReader = function(){};
ErrorReader.prototype.read = function(xhr) {
var unescapeHTML = function(str) {
return str.replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&');
}
xhr.responseText = unescapeHTML(xhr.responseText.replace(/
/, "").replace("", ""));
return {records: [], success: true};
};
this.initialConfig.errorReader = new ErrorReader();
// Now let Ext.form.Form do the rest
this.callParent(arguments);
Ext.each(this.query('field'), function(field) {
field.on('specialkey', function(field, event) {
if (event.getKey() == 13) this.up('form').netzkeOnApply();
});
});
},
afterRender: function(){
this.callParent();
// have a record to be displayed?
if (this.record) { this.netzkeSetFormValues(this.record); }
// render in display mode?
if (this.locked || this.readOnly) this.netzkeSetReadonlyMode(true);
},
netzkeOnEdit: function(){
this.netzkeSetReadonlyMode(false);
},
netzkeOnCancel: function(){
this.getForm().reset();
this.netzkeSetReadonlyMode(true, true);
},
/**
* In lockable mode, dynamically updates the toolbar depending on the read-only state.
* @method netzkeUpdateToolbar
*/
netzkeUpdateToolbar: function(){
var tbar = this.child('toolbar');
if (!tbar) return;
if ( this.inReadonlyMode ) {
// if the form in readonly mode, remove "Apply" and "Cancel"
// buttons from toolbar and add "Edit" button
var buttonToRemove = tbar.child("button[name='apply']");
if ( buttonToRemove ) {
tbar.remove( buttonToRemove );
}
var buttonToRemove = tbar.child("button[name='cancel']");
if ( buttonToRemove ) {
tbar.remove( buttonToRemove );
}
tbar.add( this.actions.edit );
} else {
// if the form editable, remove "edit" button and
// insert "apply" and "cancel" instead
var buttonIndex = tbar.items.findIndex("name", "edit");
var buttonToRemove = tbar.items.getAt(buttonIndex);
if (buttonToRemove) {
tbar.remove(buttonToRemove);
}
tbar.insert( buttonIndex, this.actions.cancel );
tbar.insert( buttonIndex, this.actions.apply );
}
tbar.updateLaytout();
},
/**
* Handler for the `apply` action.
* @method netzkeOnApply
*/
netzkeOnApply: function() {
if (this.fireEvent('apply', this)) {
var values = this.getForm().getValues();
for (var fieldName in values) {
var field = this.getForm().findField(fieldName);
// TODO: move the following checks to the server side (through the :display_only option)
// do not submit values from disabled fields
if (!field || field.disabled) {
delete values[fieldName];
}
// do not submit values from read-only association fields
if (field
&& field.name.indexOf("__") !== -1
&& (field.readOnly || !field.isXType('combobox'))
&& (!field.nestedAttribute) // except for "nested attributes"
) {
delete values[fieldName];
}
// do not submit values from displayfields
if (field.isXType('displayfield')) {
delete values[fieldName];
}
// do not submit displayOnly fields
if (field.displayOnly) {
delete values[fieldName];
}
}
// loading mask
this.setLoading(true);
// We must use a different approach when the form is multipart, as we can't use the endpoint
if (this.getForm().hasUpload()) {
this.getForm().submit({ // normal submit
url: this.netzkeEndpointUrl("submit"),
params: {
data: Ext.encode(values), // here are the correct values that may be different from display values
configs: Ext.encode(this.netzkeBuildParentConfigs())
},
failure: function(form, action){
var respObj = Ext.decode(action.response.responseText);
delete respObj.success;
this.netzkeBulkExecute(respObj);
this.setLoading(false);
},
success: function(form, action) {
var respObj = Ext.decode(action.response.responseText);
delete respObj.success;
this.netzkeBulkExecute(respObj);
},
scope: this
});
} else {
this.server.submit(Ext.apply((this.baseParams || {}), { data:Ext.encode(values) }), function(){ this.setLoading(false); });
}
}
this.fireEvent('afterApply', this);
},
/**
* Called by the server on successful submit.
* @method netzkeOnSubmitSuccess
*/
netzkeOnSubmitSuccess: function() {
this.fireEvent("submitsuccess");
if (this.mode == "lockable") this.netzkeSetReadonlyMode(true);
this.setLoading(false);
},
/**
* Sets form values (including associations).
* @method netzkeSetFormValues
*/
netzkeSetFormValues: function(values){
var assocValues = values.meta.associationValues || {};
for (var assocFieldName in assocValues) {
var assocField = this.getForm().getFields().filter('name', assocFieldName).first();
if (assocField.isXType('combobox')) {
assocField.getStore().loadData([[values[assocFieldName], assocValues[assocFieldName]]]);
delete assocField.lastQuery; // force loading the store next time user clicks the trigger
} else {
assocField.setValue(assocValues[assocFieldName]);
delete values[assocFieldName]; // we don't want this to be set once more below with setValues()
}
}
this.getForm().setValues(values);
},
/**
* Dynamically changes form's read-only state.
* @method netzkeSetReadonlyMode
* @param {Boolean} onOff
*/
netzkeSetReadonlyMode: function(onOff){
if (this.inReadonlyMode == onOff) return;
this.getForm().getFields().each(function(i){
if (i.netzkeSetReadonlyMode) i.netzkeSetReadonlyMode(onOff);
});
// this.getForm().cleanDestroyed(); // because fields inside of composite fields are not auto-cleaned!
this.updateLayout();
this.inReadonlyMode = onOff;
if (this.mode == "lockable") this.netzkeUpdateToolbar();
},
/**
* Recursively extract field names from `items`.
* @method netzkeExtractFields
* @param {Array} items
*/
netzkeExtractFields: function(items){
Ext.each(items, function(i){
if (i.items) {this.netzkeExtractFields(i.items);}
else if (i.name) {this.fieldNames.push(i.name);}
}, this);
},
/**
* Called by the server to display varidation errors.
* @method netzkeApplyFormErrors
* @param {Array} errors Array of errors as provided by the `submit` endpoint
*/
netzkeApplyFormErrors: function(errors) {
var field;
Ext.iterate(errors, function(fieldName, message){
fieldName = fieldName.underscore();
if ( field = this.getForm().findField(fieldName) ||
this.getForm().findField(fieldName.replace(/([a-z]+)([0-9])/g, '$1_$2')) ||
this.getForm().getFields().findBy(function(f){ return fieldName == f.getName().replace(/(.+)__.+/g, '$1'); }) ||
this.getForm().getFields().findBy(function(f){
str = f.getName().camelize();
first = str.substring(0,1);
matchingName = (first.toLowerCase()+str.substring(1)).underscore();
return fieldName == matchingName;
})
) {
field.markInvalid(message.join('
'));
}
}, this);
},
netzkeDisplayFormErrors: function(errors){
var body = "";
Ext.each(errors, function(error){
body += error.msg + "
"
});
this.netzkeNotify(body);
},
}