silverstripe-userforms/javascript/FieldEditor.js

556 lines
15 KiB
JavaScript
Executable File

FieldEditor = Class.create();
FieldEditor.applyTo('div.FieldEditor');
FieldEditor.prototype = {
initialize: function() {
FieldEditorField.applyToChildren(this, 'div.EditableFormField');
FieldEditorHeadingField.applyToChildren(this, 'div.EditableFormHeading');
FieldEditorRadioField.applyToChildren(this, 'div.EditableRadioField');
FieldEditorCheckboxGroupField.applyToChildren(this, 'div.EditableCheckboxGroupField');
FieldEditorDropdown.applyToChildren(this, 'div.EditableDropdown');
FieldEditorEmailField.applyToChildren(this, 'div.EditableEmailField');
FieldEditorTextField.applyToChildren(this, 'div.EditableTextField');
if( !Element.hasClassName( this, 'readonly' ) ) {
Sortable.create('Fields_fields', {tag: 'div', handle:'handle'});
$('Form_EditForm').observeMethod('BeforeSave', this.beforeSave.bind(this));
}
},
sortFields: function() {
var fieldEditor = $('Fields_fields');
if(fieldEditor) {
var i, j, div, field, editables = fieldEditor.childNodes;
for( i = 0; div = editables[i]; i++ ) {
var fields = div.getElementsByTagName('input');
/*fields[fields.length - 1].value = i;*/
for( j = 0; field = fields.item(j); j++ ) {
if( field.name == div.id + '[Sort]' ) {
field.value = i;
}
}
}
}
},
beforeSave: function() {
var fieldEditor = $('Fields_fields');
if(fieldEditor) {
this.sortFields();
var children = $('Fields_fields').childNodes;
for( var i = 0; i < children.length; ++i ) {
var child = children[i];
if( child.beforeSave )
child.beforeSave();
}
}
},
deleteOption: function( optionToRemove ) {
this.getElementsByTagName('div')[0].removeChild( optionToRemove );
}
}
FieldEditorField = Class.create();
FieldEditorField.prototype = {
initialize: function() {
var fieldInfoDiv = this.findDescendant( 'div', 'FieldInfo' );
this.titleField = this.findDescendant( 'input', 'text', element );
this.titleField.onchange = this.changeTitle.bind(this);
this.titleField.onblur = this.changeTitle.bind(this);
this.titleField.onfocus = this.focusTitle.bind(this);
this.titleField.onchange();
var links = fieldInfoDiv.getElementsByTagName('a');
this.toggler = this.findDescendant( 'a', 'toggler' );
this.fieldInfo = this.getElementsByTagName('div')[0];
this.toggler.onclick = this.toggle.bind(this);
this.extraOptions = this.getExtraOptions();
this.visible = false;
this.deleteButton = this.findDescendant('a', 'delete');
//this.style.height = "auto";
if( this.deleteButton )
this.deleteButton.onclick = this.confirmDelete.bind(this);
},
toggle: function() {
// this.parentNode.autoSize();
if( this.visible )
this.hide();
else
this.show();
this.fieldInfo.style.display = 'block';
return false;
},
show: function() {
/*this.style.height = "";
this.style.overflow = "";*/
if( this.selectedOption )
this.selectedOption.checked = true;
this.visible = true;
// var extraOptions = this.getExtraOptions();
// if( this.extraOptions )
this.extraOptions.style.display = 'block';
},
hide: function() {
this.visible = false;
// var extraOptions = this.getExtraOptions();
//if( this.extraOptions )
this.extraOptions.style.display = 'none';
},
getExtraOptions: function() {
var extraOptions = this.findDescendant('div', 'ExtraOptions');
if( extraOptions.parentNode != this )
alert("Found extra options but not this parent (" + this.id + ")");
return extraOptions;
},
confirmDelete: function() {
if( confirm( 'Are you sure you want to delete this field from the form?' ) )
this.parentNode.parentNode.deleteOption( this );
return false;
},
findDescendant: function( tag, clsName, element ) {
if( !element )
element = this;
var descendants = element.getElementsByTagName(tag);
for( var i = 0; i < descendants.length; i++ ) {
var el = descendants[i];
// alert(el.tagName + ' ' + el.className);
if( tag.toUpperCase() == el.tagName && el.className.indexOf( clsName ) != -1 )
return el;
}
return null;
},
focusTitle: function() {
if( this.titleField && this.titleField.value == this.titleField.title )
this.titleField.value = '';
},
changeTitle: function() {
if( this.titleField && this.titleField.value == '' )
this.titleField.value = this.titleField.title;
}
}
FieldEditorHeadingField = Class.extend('FieldEditorField');
FieldEditorHeadingField.prototype = {
initialize: function() {
this.FieldEditorField.initialize();
}
}
FieldEditorEmailField = Class.extend('FieldEditorField');
FieldEditorEmailField.prototype = {
initialize: function() {
this.extraOptions = this.getExtraOptions();
this.defaultText = this.getDefaultText();
this.FieldEditorField.initialize();
},
getDefaultText: function() {
var defaultField = this.getDefaultField();
if(defaultField) {
var j, nestedChild, nestedChildren = defaultField.childNodes;
for( j=0; nestedChild = nestedChildren[j]; j++) {
if (nestedChild.className == 'defaultText' )
{
return nestedChild;
}
}
}
},
getDefaultField: function() {
var i, child, children = this.getElementsByTagName('div');
for( i = 0; child = children[i]; i++){
if(child.className == 'FieldDefault'){
return child;
}
}
}
}
FieldEditorTextField = Class.extend('FieldEditorField');
FieldEditorTextField.prototype = {
initialize: function() {
this.FieldEditorField.initialize();
this.defaultText = this.getDefaultText();
this.numRows = this.extraOptions.getElementsByTagName('input')[3];
if(this.numRows) {
this.numRows.onchange = this.changedRows.bind(this);
this.oldNumRows = eval(this.numRows.value);
}
},
changedRows: function() {
var newNumRows = eval(this.numRows.value);
// TODO Show that the field is actually longer than 5 rows
if( newNumRows > 5 )
newNumRows == 5;
if( this.oldNumRows == newNumRows )
return;
if( newNumRows < 1 )
newNumRows = 1;
// resize/convert the textarea
var newType = '';
if( newNumRows == 1 )
newType = 'input';
else
newType = 'textarea'
var newDefaultText = document.createElement(newType);
newDefaultText.className = this.defaultText.className;
newDefaultText.value = this.defaultText.value;
newDefaultText.id = this.defaultText.id;
newDefaultText.name = this.defaultText.name;
if( newDefaultText.rows )
newDefaultText.rows = newNumRows;
//Does not work any more
//this.replaceChild( newDefaultText, this.defaultText );
//instead, using the following code
var defaultField = this.getDefaultField();
defaultField.replaceChild(newDefaultText, this.defaultText);
//keep other codes.
this.defaultText = newDefaultText;
this.oldNumRows = newNumRows;
},
getDefaultText: function() {
var defaultField = this.getDefaultField();
if(defaultField) {
var j, nestedChild, nestedChildren = defaultField.childNodes;
for( j=0; nestedChild = nestedChildren[j]; j++) {
if (nestedChild.className == 'defaultText' )
{
return nestedChild;
}
}
}
},
getDefaultField: function() {
var i, child, children = this.getElementsByTagName('div');
for( i = 0; child = children[i]; i++){
if(child.className == 'FieldDefault'){
return child.getElementsByTagName('div')[0];
}
}
}
}
/**
* This should extend FieldEditorField
*/
FieldEditorRadioField = Class.extend('FieldEditorField');
FieldEditorRadioField.prototype = {
initialize: function() {
this.FieldEditorField.initialize();
this.hiddenFields = this.findDescendant( 'div', 'hidden' );
var dropdownBox = this.findDescendant( 'div', 'EditableDropdownBox' );
this.optionList = dropdownBox.getElementsByTagName('ul')[0];
var options = this.optionList.getElementsByTagName('li');
if( options && options.length > 0 ) {
this.addOptionField = options[options.length - 1];
if( typeof this.addOptionField != 'undefined' && this.addOptionField.className != "AddDropdownOption" )
this.addOptionField = null;
// bind each option's delete link
for( var i = 0; i < options.length - 1; i++ ) {
var option = options[i];
var links = option.getElementsByTagName('a');
links[0].onclick = this.removeOption.bindAsEventListener(this);
}
}
// Bind method to add option at the bottom of the list
if( this.addOptionField ) {
this.addOptionLink = this.addOptionField.getElementsByTagName('a')[0];
this.addOptionTitle = this.addOptionField.getElementsByTagName('input')[0];
this.addOptionLink.onclick = this.addOption.bind(this);
}
if( !Element.hasClassName( $('Fields'), 'readonly' ) ) {
Sortable.create(this.optionList.id,{handle:'handle',tag:'li',only:'EditableFormFieldOption'});
}
this.FieldEditorField.initialize();
// find the Delete field
var hiddenFields = this.getElementsByTagName('input');
for( var i = 0; i < hiddenFields.length; i++ ) {
var field = hiddenFields[i];
if( field.name.indexOf('[Deleted\]' ) != -1 )
this.deletedOptions = field;
}
this.selectedOption = null;
$('Form_EditForm').observeMethod('BeforeSave', this.beforeSave.bind(this));
},
firstElement: function( el ) {
var node = el.firstChild;
while( !node.tagName )
node = node.nextSibling;
return node;
},
createOption: function( title, id, selected ) {
var templateNode = this.firstElement( this.hiddenFields );
var newOptionNode = templateNode.cloneNode( true );
var newNodeChildren = newOptionNode.childNodes;
for( var i = 0; i < newNodeChildren.length; i++ ) {
var child = newNodeChildren[i];
if( !child.tagName )
continue;
// input elements
if( child.tagName.toLowerCase() == 'input' ) {
if( child.className == 'text' ) {
child.name = this.id + '[' + id + '][Title]';
child.value = title;
} else if( child.type == 'checkbox' )
child.name = this.id + '[' + id + '][Default]';
else if( child.type == 'radio' ) {
child.value = id;
} else if( child.type == 'hidden' ) {
child.name = this.id + '[' + id + '][Sort]';
child.value = -1;
}
} else if ( child.tagName.toLowerCase() == 'a' ) {
child.onclick = this.removeOption.bindAsEventListener(this);
}
}
this.optionList.insertBefore( newOptionNode, this.addOptionField );
},
removeOption: function( event ) {
var target = event.srcElement;
if( !target )
target = event.target;
var entry = target.parentNode.parentNode;
var id = entry.id;
if( !id.match( '/^[0-9]+$/' ) ) {
if( this.deletedOptions.value )
this.deletedOptions.value += ',';
this.deletedOptions.value += id;
}
// remove the child from the options
this.optionList.removeChild( entry );
// remove the child from the dropdown
/*for( var i = 0; i < this.dropdown.length; i++ ) {
if( this.dropdown.options[i].text == title ) {
this.dropdown.remove(i);
return false;
}
}*/
if( !Element.hasClassName( $('Fields'), 'readonly' ) )
Sortable.create(this.optionList.id,{handle:'handle',tag:'li',only:'EditableFormFieldOption'});
// return false so it doesn't follow the link
return false;
},
addOption: function() {
if( this.addOptionTitle.value.length == 0 )
return false;
// The IDs come from the database and are the ID of the actual record
// client-side, we will need a unique identifier that can be differentiated
// from the actual database IDs, unless we just drop all records and
// recreate them
var newID = '_' + this.optionList.childNodes.length;
this.createOption( this.addOptionTitle.value, newID, this.optionList.childNodes.length == 0 );
if( !Element.hasClassName( $('Fields'), 'readonly' ) )
Sortable.create(this.optionList.id,{handle:'handle',tag:'li',only:'EditableFormFieldOption'});
this.addOptionTitle.value = '';
return false;
},
beforeSave: function() {
this.sortOptions();
},
sortOptions: function() {
var inputTags = this.optionList.getElementsByTagName('input');
var i,item,sort=0;
for(i=0;item=inputTags[i];i++) {
if(item.name.match(/\[Sort\]$/) ) {
item.value = sort++;
}
}
},
selectOption: function(newOption) {
if( this.selectedOption )
this.selectedOption.checked = false;
newOption.checked = true;
this.selectedOption = newOption;
},
selectOptionEvent: function(event) {
if(event.srcElement)
this.selectOption(event.srcElement);
else
this.selectOption(event.target);
},
updateOption: function( prefix, tempID, newID, newSort ) {
var options = this.optionList.childNodes;
for( var i = 0; i < options.length; i++ ) {
var option = options[i];
var fields = option.getElementsByTagName('input');
for( var j = 0; j < fields.length; j++ ) {
var field = fields[j];
var oldPrefix = prefix + '[' + tempID + ']';
var newPrefix = prefix + '[' + newID + ']';
if( field.name.indexOf( oldPrefix ) == 0 ) {
if( field.name.match( /\[Sort\]$/ ) )
field.value = newSort;
// rename the field
field.name = newPrefix + field.name.substring( oldPrefix.length );
} else if( field.name == prefix + '[Default]' ) {
field.value = newID;
}
}
}
}
}
FieldEditorCheckboxGroupField = Class.extend('FieldEditorRadioField');
FieldEditorDropdown = Class.extend('FieldEditorRadioField');
Behaviour.register(
{
'div.FieldEditor ul.Menu li a': {
urlForFieldMethod: function(methodName) {
return this.ownerForm().action + '/field/Fields/' + methodName + '?NewID=' + this.numNewFields;
},
ownerForm: function() {
var f = this.parentNode;
while(f && f.tagName.toLowerCase() != 'form') f = f.parentNode;
return f;
},
onclick: function() {
// get the ID of the field editor here
if( Element.hasClassName( $('Fields'), 'readonly' ) )
return false;
action = this.urlForFieldMethod("addfield") + "&Type=" + this.id + ($('SecurityID') ? '&SecurityID=' + $('SecurityID').value : '');;
statusMessage('Adding new field' );
new Ajax.Request(action, {
method: 'get',
onFailure: reportError,
onSuccess: this.appendNewField.bind(this)
});
return false;
},
appendNewField: function(response) {
this.numNewFields++;
var el = document.createElement('div');
el.innerHTML = response.responseText;
var i=0;
while(!el.childNodes[i].tagName) i++;
var newField = el.childNodes[i];
$('Fields_fields').appendChild(newField);
// Behaviour.debug();
if(newField) {
Behaviour.apply(newField,true);
FieldEditor.applyTo('div.FieldEditor');
}
// do we want to make sorting explicit?
Sortable.create('Fields_fields', {tag: 'div', handle:'handle'});
statusMessage('Added new field','good');
}
}
}
);
function reportError(request){
// More complex error for developers
statusMessage(request.responseText,'bad');
}