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.foundation.NSDictionary; import com.webobjects.foundation.NSMutableArray; import com.webobjects.foundation.NSMutableDictionary; import er.ajax.AjaxDynamicElement; import er.ajax.AjaxObserveField; import er.ajax.AjaxOption; import er.ajax.AjaxOptions; import er.ajax.AjaxUpdateContainer; import er.ajax.AjaxUtils; import er.extensions.foundation.ERXValueUtilities; /** * observeFieldID requires ERExtensions, specifically ERXWOForm * @binding elementName the container's element defaults to DIV. * @binding action the action to call when this updateContainer refreshes * @binding method - (string: defaults to 'post') The HTTP method for the request, can be either 'post' or 'get'. * @binding encoding - (string: defaults to 'utf-8') The encoding to be set in the request header. * @binding emulation - (boolean: defaults to true) If set to true, other methods than 'post' or 'get' are appended as post-data named '_method' (as used in rails) * @binding headers - (object) An object to use in order to set the request headers. * @binding isSuccess - (function) Overrides the built-in isSuccess function. * @binding evalScripts - (boolean: defaults to true) If set to true, script tags inside the response will be evaluated. * @binding evalResponse - (boolean: defaults to false) If set to true, the entire response will be evaluated. Responses with javascript content-type will be evaluated automatically. * @binding urlEncoded - (boolean: defaults to true) If set to true, the content-type header is set to www-form-urlencoded + encoding * @binding noCache - (boolean; defaults to false) If true, appends a unique noCache value to the request to prevent caching. (IE has a bad habit of caching ajax request values. Including this script and setting the noCache value to true will prevent it from caching. The server should ignore the noCache value.) * @binding async - (boolean: defaults to true) If set to false, the requests will be synchronous and freeze the browser during request. * @binding optional set to true if you want the container tags to be skipped if this is already in an update container (similar to ERXOptionalForm). * If optional is true and there is a container, it's as if this AUC doesn't exist, and only its children will render to the page. * @binding onCancel Fired when a request has been cancelled. * @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 frequency the frequency (in seconds) of a periodic update * @binding initialDelay - (number; defaults to 5000) The initial delay to wait for the request after a call to the start method * @binding delay - (number; defaults to 5000) The delay between requests and the number of ms to add if no valid data has been returned * @binding limit - (number; defaults to 60000) The maximum time the interval uses to request the server * responseTree - (element) The node list of the remote response. * responseElements - (array) An array containing all elements of the remote response. * responseHTML - (string) The content of the remote response. * responseJavaScript - (string) The portion of JavaScript from the remote response. * @binding onFailure Fired when the request failed (error status code). * @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 MTAjaxUpdateContainer extends AjaxUpdateContainer { public MTAjaxUpdateContainer(String name, NSDictionary<String, WOAssociation> associations, WOElement children) { super(name, associations, children); } public static String updateContainerID(AjaxDynamicElement element, WOComponent component) { return AjaxUpdateContainer.updateContainerID(element, component); } @Override protected 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(), "MTAjax", "scripts/plugins/spinner/spinner.css"); } } MTAjaxUtils.addScriptResourceInHead(context, context.response(), "MooTools", MTAjaxUtils.MOOTOOLS_WONDER_JS); } @Override public NSDictionary createAjaxOptions(WOComponent component) { NSMutableArray<AjaxOption> ajaxOptionsArray = new NSMutableArray<>(); ajaxOptionsArray.addObject(new AjaxOption("method", AjaxOption.STRING)); ajaxOptionsArray.addObject(new AjaxOption("frequency", AjaxOption.NUMBER)); ajaxOptionsArray.addObject(new AjaxOption("initialDelay", AjaxOption.NUMBER)); ajaxOptionsArray.addObject(new AjaxOption("delay", AjaxOption.NUMBER)); ajaxOptionsArray.addObject(new AjaxOption("limit", AjaxOption.NUMBER)); ajaxOptionsArray.addObject(new AjaxOption("encoding", AjaxOption.STRING)); ajaxOptionsArray.addObject(new AjaxOption("emulation", AjaxOption.BOOLEAN)); ajaxOptionsArray.addObject(new AjaxOption("headers", AjaxOption.ARRAY)); ajaxOptionsArray.addObject(new AjaxOption("isSuccess", AjaxOption.FUNCTION)); ajaxOptionsArray.addObject(new AjaxOption("evalScripts", AjaxOption.BOOLEAN)); ajaxOptionsArray.addObject(new AjaxOption("evalResponse", AjaxOption.BOOLEAN)); ajaxOptionsArray.addObject(new AjaxOption("urlEncoded", AjaxOption.BOOLEAN)); ajaxOptionsArray.addObject(new AjaxOption("async", AjaxOption.BOOLEAN)); ajaxOptionsArray.addObject(new AjaxOption("noCache", AjaxOption.BOOLEAN)); ajaxOptionsArray.addObject(new AjaxOption("onRequest", AjaxOption.FUNCTION)); ajaxOptionsArray.addObject(new AjaxOption("onCancel", AjaxOption.SCRIPT)); ajaxOptionsArray.addObject(new AjaxOption("onComplete", AjaxOption.SCRIPT)); ajaxOptionsArray.addObject(new AjaxOption("onSuccess", AjaxOption.FUNCTION_2)); ajaxOptionsArray.addObject(new AjaxOption("onFailure", AjaxOption.FUNCTION_1)); ajaxOptionsArray.addObject(new AjaxOption("onException", AjaxOption.FUNCTION_2)); 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("evalScripts") == null) { options.setObjectForKey("true", "evalScripts"); } AjaxUpdateContainer.expandInsertionFromOptions(options, this, component); return options; } public static NSDictionary removeDefaultOptions(NSDictionary options) { // PROTOTYPE OPTIONS NSMutableDictionary mutableOptions = options.mutableClone(); if ("'get'".equals(mutableOptions.objectForKey("method"))) { mutableOptions.removeObjectForKey("method"); } if ("true".equals(mutableOptions.objectForKey("evalScripts"))) { mutableOptions.removeObjectForKey("evalScripts"); } if ("true".equals(mutableOptions.objectForKey("async"))) { mutableOptions.removeObjectForKey("async"); } return mutableOptions; } @Override public void appendToResponse(WOResponse response, WOContext context) { WOComponent component = context.component(); if(!shouldRenderContainer(component)) { if(hasChildrenElements()) { appendChildrenToResponse(response, context); } super.appendToResponse(response, context); } else { String previousUpdateContainerID = AjaxUpdateContainer.currentUpdateContainerID(); try { String elementName = (String) valueForBinding("elementName", "div", component); String id = _containerID(context); AjaxUpdateContainer.setCurrentUpdateContainerID(_containerID(context)); response.appendContentString("<" + elementName + " "); appendTagAttributeToResponse(response, "id", id); appendTagAttributeToResponse(response, "class", valueForBinding("class", component)); appendTagAttributeToResponse(response, "style", valueForBinding("style", component)); appendTagAttributeToResponse(response, "data-updateUrl", AjaxUtils.ajaxComponentActionUrl(context)); response.appendContentString(">"); if(hasChildrenElements()) { appendChildrenToResponse(response, context); } response.appendContentString("</" + elementName + ">"); addRequiredWebResources(response, context); // super.appendToResponse(response, context); NSDictionary options = createAjaxOptions(component); Object frequency = valueForBinding("frequency", component); String observeFieldID = (String) valueForBinding("observeFieldID", component); boolean skipFunction = frequency == null && observeFieldID == null && booleanValueForBinding("skipFunction", false, component); if(!skipFunction) { AjaxUtils.appendScriptHeader(response); if(frequency != null) { boolean isNotZero = true; try { float numberFrequency = ERXValueUtilities.floatValue(frequency); if(numberFrequency == 0.0) { isNotZero = false; } } catch (RuntimeException e) { throw new IllegalStateException("Error parsing float from value : <" + frequency + ">"); } if(isNotZero) { boolean canStop = false; boolean stopped = false; if(associations().objectForKey("stopped") != null) { canStop = true; stopped = booleanValueForBinding("stopped", false, component); } response.appendContentString("MTAUC.registerPeriodic('" + id + "'," + canStop + "," + stopped + ","); AjaxOptions.appendToResponse(options, response, context); response.appendContentString(");"); } } if(observeFieldID != null) { boolean fullSubmit = booleanValueForBinding("fullSubmit", false, component); AjaxObserveField.appendToResponse(response, context, this, observeFieldID, false, id, fullSubmit, createObserveFieldOptions(component)); } response.appendContentString("MTAUC.register('" + id + "'"); NSDictionary nonDefaultOptions = AjaxUpdateContainer.removeDefaultOptions(options); if (nonDefaultOptions.count() > 0) { response.appendContentString(", "); AjaxOptions.appendToResponse(nonDefaultOptions, response, context); } response.appendContentString(");"); AjaxUtils.appendScriptFooter(response); } } finally { AjaxUpdateContainer.setCurrentUpdateContainerID(previousUpdateContainerID); } } } }