package er.ajax;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webobjects.appserver.WOActionResults;
import com.webobjects.appserver.WOComponent;
import com.webobjects.appserver.WOContext;
import com.webobjects.appserver.WORequest;
import com.webobjects.appserver.WOResponse;
import er.extensions.appserver.ERXWOContext;
import er.extensions.components.ERXComponentUtilities;
import er.extensions.components._private.ERXWOForm;
/**
* AjaxInPlace is a generalization of the AjaxInPlaceEditor. To use this component, you must wrap an ERXWOTemplate named
* "view" and an ERXWOTemplate named "edit".<br>
* For instance:
* <p>
* HTML:
*
* <pre>
* <webobject name = "ExampleInPlace">
* <webobject name = "View">View: <webobject name = "Value"/></webobject>
* <webobject name = "Edit">Edit: <webobject name = "ValueField"/></webobject>
* </webobject>
* </pre>
*
* WOD:
*
* <pre>
* ExampleInPlace : AjaxInPlace {
* }
*
* View : ERXWOTemplate {
* templateName = "view";
* }
*
* Value : WOString {
* value = value;
* }
*
* Edit : ERXWOTemplate {
* templateName = "edit";
* }
*
* ValueField : WOTextField {
* value = value;
* }
* </pre>
*
* @binding class the class used on the top container
* @binding id the id used on various parts of this component
*
* @binding saveLabel the label to show on the save button
* @binding saveAction the action to invoke on save
* @binding saveUpdateContainerID by default save updates the container specified in "id", but you can override that with this binding
* @binding saveClass the class of the save button
* @binding canSave if true, the results are saved; if false, the user is not allowed to leave edit mode
* @binding onSaveClick the action to fire when save is clicked
* @binding onSaveSuccess the javascript function to execute after a successful save
* @binding onSaveFailure the javascript function to execute after a failed save
* @binding onSaving the javascript action to fire when saving
* @binding button if true, the save action is a button; if false, it's a link
* @binding submitOnSave if true, the save button is an AjaxSubmitButton; if false, it's an AjaxUpdateLink (which will not actually submit a form -- you would have to do some work here)
* // PROTOTYPE FUNCTIONS
* @binding saveInsertion the insertion function to use on save
* @binding saveInsertionDuration the duration of the before and after insertion animation (if using insertion)
* @binding saveBeforeInsertionDuration the duration of the before insertion animation (if using insertion)
* @binding saveAfterInsertionDuration the duration of the after insertion animation (if using insertion)
*
* @binding cancelLabel the label to show on the cancel button
* @binding cancelAction the action to invoke on cancel
* @binding cancelUpdateContainerID by default cancel updates the container specified in "id", but you can override that with this binding
* @binding cancelClass the class of the cancel button
* @binding onCancelClick the action to fire when cancel is clicked
* @binding onCancelSuccess the javascript function to execute after a successful cancel
* @binding onCancelFailure the javascript function to execute after a failed cancel
* @binding onCancelling the javascript action to fire when cancelling
* // PROTOTYPE FUNCTIONS
* @binding cancelInsertion the insertion function to use on cancel
* @binding cancelInsertionDuration the duration of the before and after insertion animation (if using insertion)
* @binding cancelBeforeInsertionDuration the duration of the before insertion animation (if using insertion)
* @binding cancelAfterInsertionDuration the duration of the after insertion animation (if using insertion)
*
* @binding editClass the class of the div that you click on to trigger edit mode (yes this name sucks)
* @binding formClass the class of the form around the edit view
* @binding canEdit if true, edit mode is entered; if false, view mode remains active
* @binding editOnly if true, edit mode is locked on (and save controls don't show if it's in a parent form); if false, you can switch between edit and view mode
* @binding onEditClick the action to fire when edit mode is triggered
* @binding onEditSuccess the javascript function to execute after a successful edit
* @binding onEditFailure the javascript function to execute after a failed edit
* @binding onEditing the javascript action to fire when editing mode is loading
* // PROTOTYPE FUNCTIONS
* @binding editInsertion the insertion function to use on edit
* @binding editInsertionDuration the duration of the before and after insertion animation (if using insertion)
* @binding editBeforeInsertionDuration the duration of the before insertion animation (if using insertion)
* @binding editAfterInsertionDuration the duration of the after insertion animation (if using insertion)
*
* @binding onRefreshComplete the javascript function to execute after refreshing the container
* @binding disabled whether or not edit mode should be disabled
*
* @binding manualControl if true, it is up to you to provide click-to-edit, save, and cancel controls
* @binding manualViewControl if true, it is up to you to provide click-to-edit controls
* @binding manualEditControl if true, it is up to you to provide save and cancel controls
*
* @binding style the style of the top level container
* @binding elementName the name of the container element (defaults to "div")
* @binding formSerializer the name of the javascript function to call to serialize the form
*
* @binding onsubmit pass through onsubmit to form
*
* @author mschrag
*/
public class AjaxInPlace extends WOComponent {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(AjaxInPlace.class);
private boolean _editing;
private String _id;
private boolean _changingToEdit;
private boolean _changingToView;
private boolean _alreadyInForm;
public AjaxInPlace(WOContext context) {
super(context);
}
@Override
public boolean synchronizesVariablesWithBindings() {
return false;
}
public String elementName() {
String elementName = (String) valueForBinding("elementName");
if (elementName == null) {
elementName = "div";
}
return elementName;
}
public String id() {
if (_id == null) {
if (hasBinding("id")) {
_id = (String) valueForBinding("id");
}
else {
_id = ERXWOContext.safeIdentifierName(context(), false);
}
}
return _id;
}
@Override
public WOActionResults invokeAction(WORequest aRequest, WOContext aContext) {
_alreadyInForm = context().isInForm();
WOActionResults results = super.invokeAction(aRequest, aContext);
// MS: see appendToResponse
_id = null;
return results;
}
@Override
public void takeValuesFromRequest(WORequest aRequest, WOContext aContext) {
_alreadyInForm = context().isInForm();
super.takeValuesFromRequest(aRequest, aContext);
// MS: see appendToResponse
_id = null;
}
@Override
public void appendToResponse(WOResponse aResponse, WOContext aContext) {
_alreadyInForm = context().isInForm();
super.appendToResponse(aResponse, aContext);
// MS: id was being cached, but if the structure of the page changes,
// it can cache too aggressively. We really only care that the id
// is cached for the duration of a single R-R loop. When we're done,
// toss the value so it can be recalculated properly the next time.
_id = null;
_changingToEdit = false;
_changingToView = false;
}
public String saveUpdateContainerID() {
String saveUpdateContainerID = null;
if (hasBinding("saveUpdateContainerID")) {
saveUpdateContainerID = (String)valueForBinding("saveUpdateContainerID");
}
if (saveUpdateContainerID == null) {
saveUpdateContainerID = id();
}
return saveUpdateContainerID;
}
public String cancelUpdateContainerID() {
String cancelUpdateContainerID = null;
if (hasBinding("cancelUpdateContainerID")) {
cancelUpdateContainerID = (String)valueForBinding("cancelUpdateContainerID");
}
if (cancelUpdateContainerID == null) {
cancelUpdateContainerID = id();
}
return cancelUpdateContainerID;
}
public String saveLabel() {
String saveLabel = (String)valueForBinding("saveLabel");
if (saveLabel == null) {
saveLabel = "save";
}
return saveLabel;
}
public String formName() {
String formName = null;
if (_alreadyInForm) {
formName = ERXWOForm.formName(context(), null);
if (formName == null) {
log.warn("{} is already inside of a form, but that form has no name, so AjaxInPlace can't work properly.", id());
formName = "SetTheParentFormName";
}
}
else {
formName = id();
}
return formName;
}
public boolean button() {
return ERXComponentUtilities.booleanValueForBinding("button", true, _keyAssociations, parent());
}
public boolean submitOnSave() {
return ERXComponentUtilities.booleanValueForBinding("submitOnSave", true, _keyAssociations, parent());
}
public boolean linkOnSave() {
return !submitOnSave();
}
public boolean disableForm() {
return _alreadyInForm || linkOnSave();
}
public String updateFunctionName() {
return id() + "Update();";
}
public String editFunctionName() {
return id() + "Edit";
}
public String editFunctionCall() {
String editFunctionCall = null;
if (!disabled()) {
editFunctionCall = editFunctionName() + "()";
}
return editFunctionCall;
}
public String saveFunctionName() {
return id() + "Save";
}
public String saveFunctionCall() {
return saveFunctionName() + "()";
}
public String cancelFunctionName() {
return id() + "Cancel";
}
public String cancelFunctionCall() {
return cancelFunctionName() + "()";
}
public boolean manualControl() {
boolean manualControl = false;
if (hasBinding("manualControl")) {
manualControl = ERXComponentUtilities.booleanValueForBinding(this, "manualControl");
}
return manualControl;
}
public boolean manualEditControl() {
boolean manualEditControl = manualControl();
if (!manualEditControl && hasBinding("manualEditControl")) {
manualEditControl = ERXComponentUtilities.booleanValueForBinding(this, "manualEditControl");
}
if (_alreadyInForm && editOnly()) {
manualEditControl = true;
}
return manualEditControl;
}
public boolean manualViewControl() {
boolean manualViewControl = manualControl();
if (!manualViewControl && hasBinding("manualViewControl")) {
manualViewControl = ERXComponentUtilities.booleanValueForBinding(this, "manualViewControl");
}
return manualViewControl;
}
public boolean disabled() {
boolean disabled = false;
if (hasBinding("disabled")) {
disabled = ERXComponentUtilities.booleanValueForBinding(this, "disabled");
}
return disabled;
}
public boolean editOnly() {
return ERXComponentUtilities.booleanValueForBinding(this, "editOnly");
}
public boolean editing() {
if (hasBinding("editing")) {
_editing = ERXComponentUtilities.booleanValueForBinding(this, "editing");
}
return !disabled() && (_editing || editOnly());
}
public boolean canEdit() {
return (!hasBinding("canEdit") || ERXComponentUtilities.booleanValueForBinding(this, "canEdit"));
}
public boolean canSave() {
return (!hasBinding("canSave") || ERXComponentUtilities.booleanValueForBinding(this, "canSave"));
}
public boolean changingToEdit() {
return _changingToEdit;
}
public boolean changingToView() {
return _changingToView;
}
public void setEditing(boolean editing) {
if (canSetValueForBinding("editing")) {
setValueForBinding(Boolean.valueOf(editing), "editing");
if (hasBinding("editing")) {
editing = ERXComponentUtilities.booleanValueForBinding(this, "editing");
}
}
if (_editing != editing) {
_editing = editing;
if (_editing) {
_changingToEdit = true;
}
else {
_changingToView = true;
}
}
}
public String cleanupFunction() {
//<script>AjaxInPlace.cleanupEdit('sheetSetHeaderWrapperSave', 'sheetSetHeaderWrapperCancel');</script>
//<script>AjaxInPlace.cleanupEdit('sheetSetHeaderWrapper');</script>
return null;
}
public WOActionResults startEditing() {
if (canEdit()) {
setEditing(true);
valueForBinding("editAction"); // ignore results
}
return null;
}
public WOActionResults save() {
// check to see if we can save before firing the action (for permissions)
WOActionResults results = null;
boolean canSave = canSave();
if (canSave) {
if (hasBinding("saveAction")) {
results = (WOActionResults) valueForBinding("saveAction");
canSave = canSave();
}
// check to see if we can save after firing the action (in case validation failed or something)
if (canSave && !editOnly()) {
setEditing(false);
}
}
// ignore results
return results;
}
public WOActionResults cancel() {
WOActionResults results = (WOActionResults) valueForBinding("cancelAction");
if (!editOnly()) {
setEditing(false);
}
// ignore results
return results;
}
}