package er.ajax.mootools;
import com.webobjects.appserver.WOAssociation;
import com.webobjects.appserver.WOComponent;
import com.webobjects.appserver.WOContext;
import com.webobjects.appserver.WOElement;
import com.webobjects.appserver.WOResponse;
import com.webobjects.appserver._private.WODynamicElementCreationException;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSMutableDictionary;
import er.ajax.AjaxOption;
import er.ajax.AjaxOptions;
import er.ajax.AjaxUpdateContainer;
import er.ajax.AjaxUpdateLink;
import er.ajax.AjaxUtils;
import er.extensions.components.ERXComponentUtilities;
import er.extensions.foundation.ERXStringUtilities;
/**
* Updates a region on the screen by creating a request to an action, then returning a script that in turn creates an
* Ajax.Updater for the area. If you do not provide an action binding, it will just update the specified area.
* @binding action the action to call when the link executes
* @binding directActionName the direct action to call when link executes
* @binding updateContainerID the id of the AjaxUpdateContainer to update after performing this action
* @binding title title of the link
* @binding style css style of the link
* @binding class css class of the link
* @binding id of the link
* @binding disabled boolean defining if the link renders the tag
* @binding string string to get preprended to the contained elements
* @binding function a custom function to call that takes a single parameter that is the action url
* @binding elementName the element name to use (defaults to "a")
* @binding functionName if set, the link becomes a javascript function
* @binding button if true, this is rendered as a javascript button
* @binding async boolean defining if the update request is sent asynchronously or synchronously, defaults to true
* @binding accesskey hot key that should trigger the link (optional)
* @binding onCancel Fired when a request has been cancelled.
* @binding onClickBefore if the given function returns true, the onClick is executed. This is to support confirm(..) dialogs.
* @binding onClick JS function, called after the click on the client
* @binding onComplete Fired when the Request is completed.
* @binding onException Fired when setting a request header fails.
* @binding onFailure Fired when the request failed (error status code).
* @binding onRequest Fired when the Request is sent.
* @binding onSuccess(responseTree, responseElements, responseHTML, responseJavaScript) Fired when the Request is completed successfully.
* @binding useSpinner (boolean) use the Spinner class with this request
* @binding defaultSpinnerClass inclue the default spinner css class in the headers - if false provide your own.
* @binding spinnerOptions - (object) the options object for the Spinner class
* @binding spinnerTarget - (mixed) a string of the id for an Element or an Element reference to use instead of the one specifed in the update option. This is useful if you want to overlay a different area (or, say, the parent of the one being updated).
**/
public class MTAjaxUpdateLink extends AjaxUpdateLink {
public MTAjaxUpdateLink(String name, NSDictionary<String, WOAssociation> associations, WOElement children) {
super(name, associations, children);
}
@Override
protected NSMutableDictionary<String, String> createAjaxOptions(WOComponent component) {
NSMutableArray<AjaxOption> ajaxOptionsArray = new NSMutableArray<>();
ajaxOptionsArray.addObject(new AjaxOption("async", AjaxOption.BOOLEAN));
ajaxOptionsArray.addObject(new AjaxOption("onRequest", AjaxOption.FUNCTION));
ajaxOptionsArray.addObject(new AjaxOption("onComplete", AjaxOption.FUNCTION));
ajaxOptionsArray.addObject(new AjaxOption("onSuccess", AjaxOption.FUNCTION_2));
ajaxOptionsArray.addObject(new AjaxOption("onFailure", AjaxOption.FUNCTION));
ajaxOptionsArray.addObject(new AjaxOption("onException", AjaxOption.SCRIPT));
ajaxOptionsArray.addObject(new AjaxOption("evalScripts", AjaxOption.BOOLEAN));
ajaxOptionsArray.addObject(new AjaxOption("useSpinner", AjaxOption.BOOLEAN));
ajaxOptionsArray.addObject(new AjaxOption("spinnerTarget", AjaxOption.STRING));
ajaxOptionsArray.addObject(new AjaxOption("spinnerOptions", AjaxOption.DICTIONARY));
NSMutableDictionary<String, String> options = AjaxOption.createAjaxOptionsDictionary(ajaxOptionsArray, component, associations());
options.setObjectForKey("'get'", "method");
if (options.objectForKey("async") == null) {
options.setObjectForKey("true", "async");
}
if (options.objectForKey("evalScripts") == null) {
options.setObjectForKey("true", "evalScripts");
}
AjaxUpdateContainer.expandInsertionFromOptions(options, this, component);
return options;
}
@Override
public void addRequiredWebResources(WOResponse response, WOContext context) {
MTAjaxUtils.addScriptResourceInHead(context, context.response(), "MooTools", MTAjaxUtils.MOOTOOLS_CORE_JS);
MTAjaxUtils.addScriptResourceInHead(context, context.response(), "MooTools", MTAjaxUtils.MOOTOOLS_MORE_JS);
Boolean useSpinner = (Boolean)valueForBinding("useSpinner", Boolean.FALSE, context.component());
if(useSpinner.booleanValue()) {
Boolean useDefaultSpinnerClass = (Boolean)valueForBinding("defaultSpinnerClass", Boolean.TRUE, context.component());
if(useDefaultSpinnerClass.booleanValue()) {
AjaxUtils.addStylesheetResourceInHead(context, context.response(), "MooTools", "scripts/plugins/spinner/spinner.css");
}
}
MTAjaxUtils.addScriptResourceInHead(context, context.response(), "MooTools", MTAjaxUtils.MOOTOOLS_WONDER_JS);
}
@Override
public String onClick(WOContext context, boolean generateFunctionWrapper) {
WOComponent component = context.component();
NSMutableDictionary options = createAjaxOptions(component);
StringBuilder onClickBuffer = new StringBuilder();
String onClick = (String) valueForBinding("onClick", component);
String onClickBefore = (String) valueForBinding("onClickBefore", component);
String updateContainerID = (String) valueForBinding("updateContainerID", component);
String functionName = (String) valueForBinding("functionName", component);
String function = (String) valueForBinding("function", component);
String replaceID = (String) valueForBinding("replaceID", component);
String effect = (String) valueForBinding("effect", component);
String afterEffect = (String) valueForBinding("afterEffect", component);
String beforeEffect = (String) valueForBinding("beforeEffect", component);
if(effect != null) {
String duration = (String) valueForBinding("effectDuration", component);
String property = (String) valueForBinding("effectProperty", component);
String start = (String) valueForBinding("effectStart", component);
String mode = (String) valueForBinding("effectSlideMode", component);
MTAjaxUpdateLink.addEffect(options, effect, updateContainerID, property, start, duration, mode);
} else if(afterEffect != null) {
String duration = (String) valueForBinding("afterEffectDuration", component);
String property = (String) valueForBinding("afterEffectProperty", component);
String start = (String) valueForBinding("afterEffectStart", component);
String afterEffectID = (String) valueForBinding("afterEffectID", component);
String mode = (String) valueForBinding("effectSlideMode", component);
if(afterEffectID == null) {
afterEffectID = AjaxUpdateContainer.currentUpdateContainerID() != null ? AjaxUpdateContainer.currentUpdateContainerID() :
updateContainerID;
}
MTAjaxUpdateLink.addEffect(options, afterEffect, afterEffectID, property, start, duration, mode);
}
WOAssociation directActionNameAssociation = (WOAssociation) associations().valueForKey("directActionName");
if(beforeEffect == null && updateContainerID != null && directActionNameAssociation == null && replaceID == null
&& function == null && onClick == null && onClickBefore == null) {
NSDictionary nonDefaultOptions = MTAjaxUpdateContainer.removeDefaultOptions(options);
onClickBuffer.append("MTAUL.")
.append(generateFunctionWrapper ? "updateFunc" : "update")
.append("('").append(updateContainerID).append("', ");
AjaxOptions.appendToBuffer(nonDefaultOptions, onClickBuffer, context);
onClickBuffer.append(", '").append(context.contextID()).append('.').append(context.elementID())
.append('\'').append(')').append(';');
} else {
if(generateFunctionWrapper) {
onClickBuffer.append("function(additionalParams) {");
}
if(onClickBefore != null) {
onClickBuffer.append("if (")
.append(onClickBefore)
.append(") { ");
}
// EFFECTS
if(beforeEffect != null) {
String beforeEffectID = (String)valueForBinding("beforeEffectID", component);
String beforeEffectDuration = (String) valueForBinding("beforeEffectDuration", component);
String beforeEffectProperty = (String)valueForBinding("beforeEffectProperty", component);
String beforeEffectStart = (String)valueForBinding("beforeEffectStart", component);
if(beforeEffectID == null) {
beforeEffectID = AjaxUpdateContainer.currentUpdateContainerID() != null ?
AjaxUpdateContainer.currentUpdateContainerID() : updateContainerID;
}
if(beforeEffect.equals("tween")) {
if(beforeEffectDuration != null) {
onClickBuffer.append("$('").append(beforeEffectID)
.append("').set('tween', { duration: '")
.append(beforeEffectDuration).append("', property: '" + beforeEffectProperty + "' });");
} else {
onClickBuffer.append("$('").append(beforeEffectID).append("').set('tween', { property: '" + beforeEffectProperty + "' });");
}
onClickBuffer.append("$('").append(beforeEffectID).append("').get('tween').start(")
.append(beforeEffectStart).append(").chain(function() {");
} else if(beforeEffect.equals("morph")) {
if(beforeEffectDuration != null) {
onClickBuffer.append("$('").append(beforeEffectID).append("').set('morph', { duration: '").append(beforeEffectDuration).append("' });");
}
onClickBuffer.append("$('").append(beforeEffectID).append("').get('morph').start('." + beforeEffectStart + "'").append(").chain(function() {");
} else if(beforeEffect.equals("slide")) {
String mode = (String) valueForBinding("effectSlideMode", component);
String transition = (String) valueForBinding("beforeEffectTransition", component);
onClickBuffer.append("$('").append(beforeEffectID).append("').set('slide'");
if(beforeEffectDuration != null || mode != null) {
onClickBuffer.append(", { ");
if(beforeEffectDuration != null) {
onClickBuffer.append("duration: '").append(beforeEffectDuration).append('\'').append(mode != null || transition != null ? "," : "");
}
if(mode != null) {
onClickBuffer.append("mode: '").append(mode).append('\'').append(transition != null ? "," : "");
}
if(transition != null) {
onClickBuffer.append("transition: ").append(transition);
}
onClickBuffer.append('}');
}
onClickBuffer.append("); $('").append(beforeEffectID).append("').get('slide').slide").append(ERXStringUtilities.capitalize(beforeEffectProperty)).append("().chain(function() {");
} else if(beforeEffect.equals("highlight")) {
if(beforeEffectDuration != null) {
onClickBuffer.append("$('").append(beforeEffectID)
.append("').set('tween', { duration: '").append(beforeEffectDuration).append("', property: 'background-color'});");
} else {
onClickBuffer.append("$('").append(beforeEffectID)
.append("').set('tween', { property: 'background-color' });");
}
onClickBuffer.append("$('").append(updateContainerID).append("').get('tween').start('").append(beforeEffectProperty != null ? beforeEffectProperty : "#ffff88', '#ffffff")
.append("').chain(function() { ");
}
}
String actionUrl = null;
if(directActionNameAssociation != null) {
actionUrl = context.directActionURLForActionNamed((String) directActionNameAssociation.valueInComponent(component), ERXComponentUtilities.queryParametersInComponent(associations(), component)).replaceAll("&", "&");
} else {
actionUrl = AjaxUtils.ajaxComponentActionUrl(context);
}
actionUrl = "'" + actionUrl + "'";
if(functionName != null) {
actionUrl = actionUrl + ".addQueryParameters(additionalParams);";
}
if(function != null) {
onClickBuffer.append("return " + function + "(" + actionUrl + ")");
} else {
options.setObjectForKey(actionUrl, "url");
if(replaceID == null) {
if(updateContainerID == null) {
onClickBuffer.append("new Request(");
AjaxOptions.appendToBuffer(options, onClickBuffer, context);
onClickBuffer.append(").send();");
} else {
options.takeValueForKey("'" + updateContainerID + "'", "update");
onClickBuffer.append("new Request.HTML(");
AjaxOptions.appendToBuffer(options, onClickBuffer, context);
onClickBuffer.append(").send();");
}
} else {
options.takeValueForKey("'" + replaceID + "'", "update");
onClickBuffer.append("new Request.HTML(");
AjaxOptions.appendToBuffer(options, onClickBuffer, context);
onClickBuffer.append(").send();");
}
}
if(onClick != null) {
onClickBuffer.append(';')
.append(onClick);
}
if(beforeEffect != null) {
onClickBuffer.append("});");
}
if(onClickBefore != null) {
onClickBuffer.append(" } ");
}
if(generateFunctionWrapper) {
onClickBuffer.append('}');
}
}
return onClickBuffer.toString();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void addEffect(NSMutableDictionary options, String effect, String updateContainerID, String effectProperty, String effectStart, String duration, String mode) {
if(effect != null) {
if(options.objectForKey("onSuccess") != null) {
throw new WODynamicElementCreationException("You cannot specify both an effect and a custom onSuccess function.");
}
if(updateContainerID == null) {
throw new WODynamicElementCreationException("You cannot specify an effect without an updateContainerID.");
}
StringBuilder effectBuffer = new StringBuilder();
effectBuffer.append("function() { ");
if(effect.equals("tween")) {
if(duration != null) {
effectBuffer.append("$('").append(updateContainerID).append("').set('tween', { duration: '").append(duration).append("', property: '")
.append(effectProperty).append("' });");
} else {
effectBuffer.append("$('").append(updateContainerID).append("').set('tween', { property: '").append(effectProperty).append("' });");
}
effectBuffer.append("$('").append(updateContainerID).append("').get('tween').start(" + effectStart + ");");
} else if(effect.equals("morph")) {
if(duration != null) {
effectBuffer.append("$('").append(updateContainerID).append("').set('morph', { duration: '").append(duration).append("'});");
}
effectBuffer.append("$('").append(updateContainerID).append("').get('morph').start('." + effectStart + "');");
} else if(effect.equals("slide")) {
effectBuffer.append("$('").append(updateContainerID).append("').get('slide').slide").append(ERXStringUtilities.capitalize(effectProperty)).append("(); ");
} else if(effect.equals("highlight")) {
effectBuffer.append("$('").append(updateContainerID).append("').highlight(").append(effectProperty != null ? effectProperty : "").append(");");
}
effectBuffer.append('}');
options.setObjectForKey(effectBuffer.toString(), "onSuccess");
}
}
}