diff --git a/Examples/Ajax/AjaxExample/Components/AjaxExampleComponent.wo/AjaxExampleComponent.html b/Examples/Ajax/AjaxExample/Components/AjaxExampleComponent.wo/AjaxExampleComponent.html
index e025570a5d4..8fabe718cee 100644
--- a/Examples/Ajax/AjaxExample/Components/AjaxExampleComponent.wo/AjaxExampleComponent.html
+++ b/Examples/Ajax/AjaxExample/Components/AjaxExampleComponent.wo/AjaxExampleComponent.html
@@ -56,6 +56,7 @@
Ajax Updates
Container in Container
Update Trigger
+ Multiple container updates
Autocompletion
Dependent Popups
Slider
diff --git a/Examples/Ajax/AjaxExample/Components/AjaxExampleComponent.wo/AjaxExampleComponent.wod b/Examples/Ajax/AjaxExample/Components/AjaxExampleComponent.wo/AjaxExampleComponent.wod
index 83d0c8558fc..758874c084e 100644
--- a/Examples/Ajax/AjaxExample/Components/AjaxExampleComponent.wo/AjaxExampleComponent.wod
+++ b/Examples/Ajax/AjaxExample/Components/AjaxExampleComponent.wo/AjaxExampleComponent.wod
@@ -14,6 +14,10 @@ UpdateTriggerLink : WOHyperlink {
pageName = "UpdateTriggerExample";
}
+MultiUpdateExample : WOHyperlink {
+ pageName = "MultiUpdateExample";
+}
+
AutocompletionLink : WOHyperlink {
pageName = "AutoCompleteExample";
}
diff --git a/Examples/Ajax/AjaxExample/Components/MultiUpdateExample.wo/MultiUpdateExample.html b/Examples/Ajax/AjaxExample/Components/MultiUpdateExample.wo/MultiUpdateExample.html
new file mode 100644
index 00000000000..bdb5ccca0d8
--- /dev/null
+++ b/Examples/Ajax/AjaxExample/Components/MultiUpdateExample.wo/MultiUpdateExample.html
@@ -0,0 +1,32 @@
+
+
+ This example demonstrates the AjaxTrigger component, which allows you to enqueue a series of updates to
+ occur. Note that these happen in series as multiple requests, not as a single request, so you will want
+ to limit the number of updates you do in a single pass.
+
+
+
+ Container 1
+
+
+
+ , and also update:
+
+ Container 2
+
+
+ Container 3
+
+
+
+
+
+ Container 2
+
+
+
+
+ Container 3
+
+
+
\ No newline at end of file
diff --git a/Examples/Ajax/AjaxExample/Components/MultiUpdateExample.wo/MultiUpdateExample.wod b/Examples/Ajax/AjaxExample/Components/MultiUpdateExample.wo/MultiUpdateExample.wod
new file mode 100644
index 00000000000..e89450bf8c9
--- /dev/null
+++ b/Examples/Ajax/AjaxExample/Components/MultiUpdateExample.wo/MultiUpdateExample.wod
@@ -0,0 +1,47 @@
+AjaxExampleComponent : AjaxExampleComponent {
+ title = "Update Trigger";
+}
+
+AjaxUpdateContainer : AjaxUpdateContainer {
+ id = "container1";
+ style = "background: green; color: white; padding: 10px";
+}
+
+Now : WOString {
+ value = now;
+}
+
+WOForm : WOForm {
+ multipleSubmit = true;
+}
+
+AjaxSubmitButton : AjaxSubmitButton {
+ updateContainerID = containerIdsToUpdate;
+ value = "Update Container 1";
+}
+
+WOCheckBox : WOCheckBox {
+ checked = updateContainer2;
+}
+
+WOCheckBox1 : WOCheckBox {
+ checked = updateContainer3;
+}
+
+AjaxUpdateContainer1 : AjaxUpdateContainer {
+ id = "container2";
+ style = "background: blue; color: white; padding: 10px";
+}
+
+Now1 : WOString {
+ value = now;
+}
+
+AjaxUpdateContainer2 : AjaxUpdateContainer {
+ id = "container3";
+ style = "background: red; color: white; padding: 10px";
+}
+
+Now2 : WOString {
+ value = now;
+}
diff --git a/Examples/Ajax/AjaxExample/Components/MultiUpdateExample.wo/MultiUpdateExample.woo b/Examples/Ajax/AjaxExample/Components/MultiUpdateExample.wo/MultiUpdateExample.woo
new file mode 100644
index 00000000000..a076a051a7e
--- /dev/null
+++ b/Examples/Ajax/AjaxExample/Components/MultiUpdateExample.wo/MultiUpdateExample.woo
@@ -0,0 +1,4 @@
+{
+ "WebObjects Release" = "WebObjects 5.0";
+ encoding = "UTF-8";
+}
\ No newline at end of file
diff --git a/Examples/Ajax/AjaxExample/Components/UpdateTriggerExample.wo/UpdateTriggerExample.html b/Examples/Ajax/AjaxExample/Components/UpdateTriggerExample.wo/UpdateTriggerExample.html
index 76793a85ee5..d400957d473 100644
--- a/Examples/Ajax/AjaxExample/Components/UpdateTriggerExample.wo/UpdateTriggerExample.html
+++ b/Examples/Ajax/AjaxExample/Components/UpdateTriggerExample.wo/UpdateTriggerExample.html
@@ -1,8 +1,8 @@
- This example demonstrates the AjaxTrigger component, which allows you to enqueue a series of updates to
- occur. Note that these happen in series as multiple requests, not as a single request, so you will want
- to limit the number of updates you do in a single pass.
+ AjaxTrigger gives you multiple container updates in one series. This exmaple shows, how you can
+ achieve the same result in one RR cycle by supplying a NSArray of container ids (alternatively a
+ comma separate string with multiple container ids). You see from the timestamps that all updates are generated at exactly the same time.
diff --git a/Examples/Ajax/AjaxExample/Sources/MultiUpdateExample.java b/Examples/Ajax/AjaxExample/Sources/MultiUpdateExample.java
new file mode 100644
index 00000000000..79c6a79c8bf
--- /dev/null
+++ b/Examples/Ajax/AjaxExample/Sources/MultiUpdateExample.java
@@ -0,0 +1,56 @@
+import com.webobjects.appserver.WOComponent;
+import com.webobjects.appserver.WOContext;
+import com.webobjects.foundation.NSArray;
+import com.webobjects.foundation.NSMutableArray;
+
+public class MultiUpdateExample extends WOComponent {
+
+ private NSMutableArray _updateContainerIDs = new NSMutableArray<>("container1");
+ private long now;
+
+ public MultiUpdateExample(WOContext context) {
+ super(context);
+ }
+
+ @Override
+ public void awake() {
+ now = System.currentTimeMillis();
+
+ super.awake();
+ }
+
+ public long now() {
+ return now;
+ }
+
+ public NSArray containerIdsToUpdate() {
+ return _updateContainerIDs;
+ }
+
+ public void setUpdateContainer2(boolean updateContainer2) {
+ setUpdateContainer("container2", updateContainer2);
+ }
+
+ public boolean updateContainer2() {
+ return _updateContainerIDs.containsObject("container2");
+ }
+
+ public void setUpdateContainer3(boolean updateContainer3) {
+ setUpdateContainer("container3", updateContainer3);
+ }
+
+ public boolean updateContainer3() {
+ return _updateContainerIDs.containsObject("container3");
+ }
+
+ protected void setUpdateContainer(String id, boolean updateContainer3) {
+ if (updateContainer3) {
+ if (!_updateContainerIDs.containsObject(id)) {
+ _updateContainerIDs.addObject(id);
+ }
+ }
+ else {
+ _updateContainerIDs.removeObject(id);
+ }
+ }
+}
diff --git a/Examples/Ajax/AjaxExample/Sources/UpdateTriggerExample.java b/Examples/Ajax/AjaxExample/Sources/UpdateTriggerExample.java
index 98812e9ef93..8ded5b616e5 100644
--- a/Examples/Ajax/AjaxExample/Sources/UpdateTriggerExample.java
+++ b/Examples/Ajax/AjaxExample/Sources/UpdateTriggerExample.java
@@ -2,18 +2,25 @@
import com.webobjects.appserver.WOContext;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSMutableArray;
-import com.webobjects.foundation.NSTimestamp;
public class UpdateTriggerExample extends WOComponent {
private NSMutableArray _updateContainerIDs = new NSMutableArray<>();
+ private long now;
public UpdateTriggerExample(WOContext context) {
super(context);
}
- public NSTimestamp now() {
- return new NSTimestamp();
+ @Override
+ public void awake() {
+ now = System.currentTimeMillis();
+
+ super.awake();
+ }
+
+ public long now() {
+ return now;
}
public NSArray otherIDsToUpdate() {
diff --git a/Frameworks/Ajax/Ajax/Components/AjaxObserveField.api b/Frameworks/Ajax/Ajax/Components/AjaxObserveField.api
index 11ac5ffa889..d8c99f009fb 100644
--- a/Frameworks/Ajax/Ajax/Components/AjaxObserveField.api
+++ b/Frameworks/Ajax/Ajax/Components/AjaxObserveField.api
@@ -18,5 +18,6 @@
+
\ No newline at end of file
diff --git a/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxObserveField.java b/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxObserveField.java
index 9e2af466e17..43889241e7f 100644
--- a/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxObserveField.java
+++ b/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxObserveField.java
@@ -62,7 +62,8 @@
* @binding style CSS style to use on the container. (Only used if you leave off observeFieldID)
* @binding onCreate Takes a JavaScript function which is called after the form has been serialized,
* but before the Ajax request is sent to the server. Useful e.g. if you want to disable the
- * form while the Ajax request is running.
+ * form while the ajax request is running.
+ * @binding actOnInput When true, input events in text input fields lead to an immediate action
*/
public class AjaxObserveField extends AjaxDynamicElement {
public AjaxObserveField(String name, NSDictionary associations, WOElement children) {
@@ -102,6 +103,7 @@ public void appendToResponse(WOResponse response, WOContext context) {
String updateContainerID = AjaxUpdateContainer.updateContainerID(this, component);
NSMutableDictionary options = createAjaxOptions(component);
boolean fullSubmit = booleanValueForBinding("fullSubmit", false, component);
+ boolean actOnInput = booleanValueForBinding("actOnInput", false, component);
boolean observeFieldDescendents;
if (observeFieldID != null) {
observeFieldDescendents = false;
@@ -125,11 +127,14 @@ public void appendToResponse(WOResponse response, WOContext context) {
response.appendContentString("" + elementName + ">");
}
AjaxUtils.appendScriptHeader(response);
- AjaxObserveField.appendToResponse(response, context, this, observeFieldID, observeFieldDescendents, updateContainerID, fullSubmit, options);
+ AjaxObserveField.appendToResponse(response, context, this, observeFieldID, observeFieldDescendents, updateContainerID, fullSubmit, options, actOnInput);
AjaxUtils.appendScriptFooter(response);
}
- public static void appendToResponse(WOResponse response, WOContext context, AjaxDynamicElement element, String observeFieldID, boolean observeDescendentFields, String updateContainerID, boolean fullSubmit, NSMutableDictionary options) {
+ public static void appendToResponse(WOResponse response, WOContext context, AjaxDynamicElement element, String observeFieldID, boolean observeDescendentFields, String updateContainerID, boolean fullSubmit, NSMutableDictionary options) {
+ appendToResponse(response, context, element, observeFieldID, observeDescendentFields, updateContainerID, fullSubmit, options, false);
+ }
+ public static void appendToResponse(WOResponse response, WOContext context, AjaxDynamicElement element, String observeFieldID, boolean observeDescendentFields, String updateContainerID, boolean fullSubmit, NSMutableDictionary options, boolean actOnInput) {
WOComponent component = context.component();
String submitButtonName = nameInContext(context, component, element);
NSMutableDictionary observerOptions = new NSMutableDictionary<>();
@@ -158,7 +163,7 @@ public static void appendToResponse(WOResponse response, WOContext context, Ajax
response.appendContentString(observeDelay);
response.appendContentString(", ");
AjaxOptions.appendToResponse(observerOptions, response, context);
- response.appendContentString(");");
+ response.appendContentString(", " + actOnInput +");");
}
public static String nameInContext(WOContext context, WOComponent component, AjaxDynamicElement element) {
diff --git a/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxResponse.java b/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxResponse.java
index 8a0221c94e9..4ef2446c8a2 100644
--- a/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxResponse.java
+++ b/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxResponse.java
@@ -1,7 +1,9 @@
package er.ajax;
import java.util.Enumeration;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -12,11 +14,13 @@
import com.webobjects.appserver.WOResponse;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSMutableDictionary;
+import com.webobjects.foundation.NSRange;
import er.extensions.appserver.ERXResponse;
import er.extensions.appserver.ERXWOContext;
import er.extensions.appserver.ajax.ERXAjaxApplication;
import er.extensions.appserver.ajax.ERXAjaxApplication.ERXAjaxResponseDelegate;
+import er.extensions.appserver.ajax.ERXAjaxSession;
/**
* AjaxResponse provides support for performing an AjaxUpdate in the same response
@@ -68,7 +72,7 @@ public WOResponse generateResponse() {
_content = new StringBuilder();
NSMutableDictionary userInfo = ERXWOContext.contextDictionary();
userInfo.setObjectForKey(Boolean.TRUE, AjaxResponse.AJAX_UPDATE_PASS);
- WOActionResults woactionresults = WOApplication.application().invokeAction(_request, _context);
+ WOApplication.application().invokeAction(_request, _context);
_content.append(originalContent);
if (_responseAppenders != null) {
Enumeration responseAppendersEnum = _responseAppenders.objectEnumerator();
@@ -77,17 +81,74 @@ public WOResponse generateResponse() {
responseAppender.appendToResponse(this, _context);
}
}
- if (_contentLength() == 0) {
+
+ int length = ((CharSequence)_content).length();
+ if (length == 0) {
setStatus(HTTP_STATUS_INTERNAL_ERROR);
log.warn("You performed an Ajax update, but no response was generated. A common cause of this is that you spelled your updateContainerID wrong. You specified a container ID '" + AjaxUpdateContainer.updateContainerID(_request) + "'.");
}
+
}
finally {
_context._setSenderID(originalSenderID);
}
+ } else
+ {
+ List updateContainerIDList = AjaxUpdateContainer.updateContainerIDList(_context.request());
+
+ if(updateContainerIDList != null)
+ {
+ _context.request().setHeader(StringUtils.join(updateContainerIDList, ','), ERXAjaxSession.PAGE_REPLACEMENT_CACHE_LOOKUP_KEY);
+
+ WOApplication.application().appendToResponse(this, _context);
+
+ StringBuilder c2 = new StringBuilder();
+ boolean firstUC = true;
+
+ for(String id : updateContainerIDList)
+ {
+ NSRange r = AjaxUpdateContainer.rangeForContainerID(_request, id);
+
+ if(r != null)
+ {
+ StringBuilder c = new StringBuilder(_content.substring(r.location(), r.location()+r.length()));
+ fixLeadingWhiteSpaces(c);
+
+ if(firstUC)
+ {
+ c2.append(c);
+ firstUC = false;
+ } else
+ {
+ c2.append("\n\n");
+ }
+ }
+ }
+
+ _content = c2;
+ }
}
+
+ if(isHTML())
+ fixLeadingWhiteSpaces(_content);
+
return this;
}
+
+ public int contentLength()
+ {
+ return _content.length();
+ }
+
+ // Some older Browsers do have problems with leading white space characters in Ajax Responses
+ // so we remove them on HTML responses
+ private void fixLeadingWhiteSpaces(StringBuilder sb)
+ {
+ while(sb.length() > 0 && Character.isWhitespace(sb.charAt(0)))
+ sb.deleteCharAt(0);
+ }
public static boolean isAjaxUpdatePass(WORequest request) {
return ERXWOContext.contextDictionary().valueForKey(AjaxResponse.AJAX_UPDATE_PASS) != null;
@@ -153,32 +214,22 @@ public void appendScriptFooterIfNecessary() {
}
/**
- * Convenience method that calls AjaxUtils.updateDomElement with this request.
- *
- * @param id
- * ID of the DOM element to update
- * @param value
- * The new value
- * @param numberFormat
- * optional number format to format the value with
- * @param dateFormat
- * optional date format to format the value with
- * @param valueWhenEmpty
- * string to use when value is null
- *
- * @see er.ajax.AjaxUtils#updateDomElement(WOResponse, String, Object, String, String, String)
+ * Convenience method that calls AjaxUtils.updateDomElement with this request.
+ * @param id
+ * @param value
+ * @param numberFormat
+ * @param dateFormat
+ * @param valueWhenEmpty
+ * @see AjaxUtils#updateDomElement
*/
public void updateDomElement(String id, Object value, String numberFormat, String dateFormat, String valueWhenEmpty) {
AjaxUtils.updateDomElement(this, id, value, numberFormat, dateFormat, valueWhenEmpty);
}
/**
- * Convenience method that calls updateDomElement with no formatters and no valueWhenEmpty string.
- *
- * @param id
- * ID of the DOM element to update
- * @param value
- * The new value
+ * Convenience method that calls updateDomElement with no formatters and no valueWhenEmpty string.
+ * @param id
+ * @param value
*/
public void updateDomElement(String id, Object value) {
updateDomElement(id, value, null, null, null);
diff --git a/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxUpdateContainer.java b/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxUpdateContainer.java
index 0f62f2f71c3..4f49876b805 100644
--- a/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxUpdateContainer.java
+++ b/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxUpdateContainer.java
@@ -1,5 +1,8 @@
package er.ajax;
+import java.util.Arrays;
+import java.util.List;
+
import com.webobjects.appserver.WOActionResults;
import com.webobjects.appserver.WOAssociation;
import com.webobjects.appserver.WOComponent;
@@ -10,6 +13,7 @@
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSMutableDictionary;
+import com.webobjects.foundation.NSRange;
import er.extensions.appserver.ERXWOContext;
import er.extensions.appserver.ajax.ERXAjaxApplication;
@@ -29,6 +33,7 @@
* @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 disabled set to true if you do not want the update container to be rendered at all (defaults to false)
* @binding frequency the frequency (in seconds) of a periodic update
* @binding decay a multiplier (default is one) applied to the frequency if the response of the update is unchanged
* @binding stopped determines whether a periodic update container loads as stopped.
@@ -45,13 +50,22 @@ public AjaxUpdateContainer(String name, NSDictionary asso
*/
@Override
protected void addRequiredWebResources(WOResponse response, WOContext context) {
- addScriptResourceInHead(context, response, "prototype.js");
- addScriptResourceInHead(context, response, "effects.js");
- addScriptResourceInHead(context, response, "wonder.js");
+ if (shouldRenderContainer(context.component())) {
+ addScriptResourceInHead(context, response, "prototype.js");
+ addScriptResourceInHead(context, response, "effects.js");
+ addScriptResourceInHead(context, response, "wonder.js");
+ }
}
protected boolean shouldRenderContainer(WOComponent component) {
- boolean renderContainer = !booleanValueForBinding("optional", false, component) || AjaxUpdateContainer.currentUpdateContainerID() == null;
+ boolean renderContainer = false;
+ if(!booleanValueForBinding("disabled", false, component))
+ {
+ // if 'disabled' == false: old behaviour:
+ renderContainer =
+ !booleanValueForBinding("optional", false, component) ||
+ AjaxUpdateContainer.currentUpdateContainerID() == null;
+ }
return renderContainer;
}
@@ -166,6 +180,10 @@ public NSMutableDictionary createObserveFieldOptions(WOComponent component) {
@Override
public void appendToResponse(WOResponse response, WOContext context) {
WOComponent component = context.component();
+ List updateContainerIDList = updateContainerIDList(context.request());
+ boolean markContentRange = response instanceof AjaxResponse && updateContainerIDList != null && updateContainerIDList.contains(valueForBinding("id", component));
+ int contentLength1 = 0;
+
if (!shouldRenderContainer(component)) {
if (hasChildrenElements()) {
appendChildrenToResponse(response, context);
@@ -185,9 +203,17 @@ public void appendToResponse(WOResponse response, WOContext context) {
appendTagAttributeToResponse(response, "data-updateUrl", AjaxUtils.ajaxComponentActionUrl(context));
// appendTagAttributeToResponse(response, "woElementID", context.elementID());
response.appendContentString(">");
+
+ if(markContentRange)
+ contentLength1 = ((AjaxResponse)response).contentLength();
+
if (hasChildrenElements()) {
appendChildrenToResponse(response, context);
}
+
+ if(markContentRange)
+ setRangeForContainerID(context.request(), (String) valueForBinding("id", component), new NSRange(contentLength1, ((AjaxResponse)response).contentLength() - contentLength1));
+
response.appendContentString("" + elementName + ">");
super.appendToResponse(response, context);
@@ -230,7 +256,8 @@ public void appendToResponse(WOResponse response, WOContext context) {
if (observeFieldID != null) {
boolean fullSubmit = booleanValueForBinding("fullSubmit", false, component);
- AjaxObserveField.appendToResponse(response, context, this, observeFieldID, false, id, fullSubmit, createObserveFieldOptions(component));
+ boolean actOnInput = booleanValueForBinding("actOnInput", false, component);
+ AjaxObserveField.appendToResponse(response, context, this, observeFieldID, false, id, fullSubmit, createObserveFieldOptions(component), actOnInput);
}
response.appendContentString("AUC.register('" + id + "'");
@@ -272,6 +299,7 @@ public WOActionResults handleRequest(WORequest request, WOContext context) {
response.appendContentString(onRefreshComplete);
AjaxUtils.appendScriptFooter(response);
}
+
if (AjaxModalDialog.isInDialog(context)) {
AjaxUtils.appendScriptHeader(response);
response.appendContentString("AMD.contentUpdated();");
@@ -295,9 +323,45 @@ public static String updateContainerID(WORequest request) {
public static void setUpdateContainerID(WORequest request, String updateContainerID) {
if (updateContainerID != null) {
- ERXWOContext.contextDictionary().setObjectForKey(updateContainerID, ERXAjaxApplication.KEY_UPDATE_CONTAINER_ID);
+ NSMutableDictionary userInfo = ERXWOContext.contextDictionary();
+
+ if(updateContainerID.indexOf(",") >= 0)
+ userInfo.setObjectForKey(Arrays.asList(updateContainerID.split(",")), CONTAINER_LIST_KEY);
+ else
+ userInfo.setObjectForKey(updateContainerID, ERXAjaxApplication.KEY_UPDATE_CONTAINER_ID);
+ }
+ }
+
+ public static List updateContainerIDList(WORequest request)
+ {
+ NSMutableDictionary userInfo = ERXWOContext.contextDictionary();
+ return (List) userInfo.objectForKey(CONTAINER_LIST_KEY);
+ }
+
+ public static void setRangeForContainerID(WORequest request, String updateContainerID, NSRange range)
+ {
+ NSMutableDictionary userInfo = ERXWOContext.contextDictionary();
+ NSMutableDictionary rangeDict = (NSMutableDictionary) userInfo.objectForKey(CONTAINER_RANGEDICT_KEY);
+ if(rangeDict == null)
+ {
+ rangeDict = new NSMutableDictionary();
+ userInfo.setObjectForKey(rangeDict, CONTAINER_RANGEDICT_KEY);
}
+
+ rangeDict.setObjectForKey(range, updateContainerID);
}
+
+ public static NSRange rangeForContainerID(WORequest request, String updateContainerID)
+ {
+ NSMutableDictionary userInfo = ERXWOContext.contextDictionary();
+ NSMutableDictionary rangeDict = (NSMutableDictionary) userInfo.objectForKey(CONTAINER_RANGEDICT_KEY);
+ if(rangeDict != null)
+ return rangeDict.objectForKey(updateContainerID);
+ return null;
+ }
+
+ private static final String CONTAINER_LIST_KEY = "_ul";
+ private static final String CONTAINER_RANGEDICT_KEY = "_rd";
public static boolean hasUpdateContainerID(WORequest request) {
return AjaxUpdateContainer.updateContainerID(request) != null;
@@ -321,7 +385,25 @@ public static String updateContainerID(AjaxDynamicElement element, WOComponent c
}
public static String updateContainerID(AjaxDynamicElement element, String bindingName, WOComponent component) {
- String updateContainerID = (String) element.valueForBinding("updateContainerID", component);
+ Object valueForBinding = element.valueForBinding("updateContainerID", component);
+ String updateContainerID = null;
+
+ if(valueForBinding instanceof String)
+ updateContainerID = (String) valueForBinding;
+ if(valueForBinding instanceof List>)
+ {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for(String s : (List extends String>)valueForBinding)
+ {
+ if(!first)
+ sb.append(',');
+ else
+ first = false;
+ sb.append(s);
+ }
+ updateContainerID = sb.toString();
+ }
return AjaxUpdateContainer.updateContainerID(updateContainerID);
}
diff --git a/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxUpdateTrigger.java b/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxUpdateTrigger.java
index 10cb40435a4..6a4c8a6b0dc 100644
--- a/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxUpdateTrigger.java
+++ b/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxUpdateTrigger.java
@@ -9,6 +9,7 @@
import com.webobjects.appserver.WODynamicElement;
import com.webobjects.appserver.WOElement;
import com.webobjects.appserver.WOResponse;
+import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import er.extensions.components.ERXComponentUtilities;
@@ -23,7 +24,7 @@
* edit mode and trigger all of the other update containers to update,
* reflecting their new non-editable status.
*
- * @binding updateContainerIDs an array of update container IDs to update
+ * @binding updateContainerIDs an array of update container IDs to update / or a comma separated string of container IDs
* @binding resetAfterUpdate if true, the array of IDs will be cleared after appendToResponse
*
* @author mschrag
@@ -44,7 +45,7 @@ public AjaxUpdateTrigger(String name, NSDictionary associ
public void appendToResponse(WOResponse response, WOContext context) {
super.appendToResponse(response, context);
WOComponent component = context.component();
- List updateContainerIDs = (List) _updateContainerIDs.valueInComponent(component);
+ List updateContainerIDs = getUpdateContainerIds(component);
if (updateContainerIDs != null && updateContainerIDs.size() > 0) {
AjaxUtils.appendScriptHeader(response);
Iterator updateContainerIDEnum = updateContainerIDs.iterator();
@@ -64,4 +65,22 @@ public void appendToResponse(WOResponse response, WOContext context) {
}
}
+ @SuppressWarnings("unchecked")
+ private List getUpdateContainerIds(WOComponent component)
+ {
+ Object value = _updateContainerIDs.valueInComponent(component);
+
+ if(value instanceof List)
+ {
+ return (List) value;
+ }
+
+ if(value instanceof String)
+ {
+ return new NSArray(((String)value).split(","));
+ }
+
+ throw new IllegalArgumentException("Invalid argument for 'updateContainerIDs' given");
+ }
+
}
diff --git a/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxValue.java b/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxValue.java
index 16ed6eddfd4..e1a33c0bacd 100644
--- a/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxValue.java
+++ b/Frameworks/Ajax/Ajax/Sources/er/ajax/AjaxValue.java
@@ -32,6 +32,26 @@ public static String javaScriptEscaped(Object obj) {
escapedValue = "'" + escapedValue + "'";
return escapedValue;
}
+
+ public static String replaceCRLF(Object obj) {
+ String escapedValue = String.valueOf(obj);
+ escapedValue = escapedValue.replaceAll("\r", "\\\\r");
+ escapedValue = escapedValue.replaceAll("\n", "\\\\n");
+ return escapedValue;
+ }
+
+ public static String javaScriptAndHTMLEscaped(Object obj)
+ {
+ String escapedValue = String.valueOf(obj);
+ escapedValue = escapedValue.replaceAll("\\\\", "\\\\\\\\");
+ escapedValue = escapedValue.replaceAll("<", "\\\\x3C");
+ escapedValue = escapedValue.replaceAll(">", "\\\\x3E");
+ escapedValue = escapedValue.replaceAll("\r", "\\\\r");
+ escapedValue = escapedValue.replaceAll("\n", "\\\\n");
+ escapedValue = escapedValue.replaceAll("'", "\\\\'");
+ escapedValue = "'" + escapedValue + "'";
+ return escapedValue;
+ }
/**
* Creates AjaxValue for value with the type guessed at.
diff --git a/Frameworks/Ajax/Ajax/WebServerResources/wonder.js b/Frameworks/Ajax/Ajax/WebServerResources/wonder.js
index 0a9d853ce51..ded85cba8db 100644
--- a/Frameworks/Ajax/Ajax/WebServerResources/wonder.js
+++ b/Frameworks/Ajax/Ajax/WebServerResources/wonder.js
@@ -338,14 +338,21 @@ var AjaxUpdateLink = {
},
update: function(id, options, elementID, queryParams) {
- var updateElement = $(id);
+ var firstId = id;
+ var updateElementList = id.split(",");
+ if(updateElementList.length > 1)
+ {
+ firstId = updateElementList[0];
+ }
+
+ var updateElement = $(firstId);
if (updateElement == null) {
alert('There is no element on this page with the id "' + id + '".');
}
- AjaxUpdateLink._update(id, updateElement.getAttribute('data-updateUrl'), options, elementID, queryParams);
+ AjaxUpdateLink._update(id, firstId, updateElement.getAttribute('data-updateUrl'), options, elementID, queryParams);
},
- _update: function(id, actionUrl, options, elementID, queryParams) {
+ _update: function(id, firstId, actionUrl, options, elementID, queryParams) {
if (elementID) {
actionUrl = actionUrl.sub(/[^\/]+$/, elementID);
}
@@ -354,10 +361,17 @@ var AjaxUpdateLink = {
actionUrl = actionUrl.addQueryParameters('_r='+ id);
}
else {
- actionUrl = actionUrl.addQueryParameters('_u='+ id);
+ if(id.indexOf(",") >= 0)
+ {
+ actionUrl = actionUrl.addQueryParameters('_ul='+ id);
+ }
+ else
+ {
+ actionUrl = actionUrl.addQueryParameters('_u='+ id);
+ }
}
actionUrl = actionUrl.addQueryParameters(new Date().getTime());
- new Ajax.Updater(id, actionUrl, AjaxOptions.defaultOptions(options));
+ new Ajax.Updater(firstId, actionUrl, AjaxOptions.defaultOptions(options));
},
request: function(actionUrl, options, elementID, queryParams) {
@@ -392,7 +406,10 @@ var AjaxSubmitButton = {
actionUrl = actionUrl.addQueryParameters('_r=' + id);
}
else {
- actionUrl = actionUrl.addQueryParameters('_u=' + id);
+ if(id.indexOf(",") >= 0)
+ actionUrl = actionUrl.addQueryParameters('_ul=' + id);
+ else
+ actionUrl = actionUrl.addQueryParameters('_u=' + id);
}
}
actionUrl = actionUrl.addQueryParameters(new Date().getTime());
@@ -448,13 +465,17 @@ var AjaxSubmitButton = {
},
update: function(id, form, queryParams, options) {
- var updateElement = $(id);
+ var firstId = id;
+ var updateElementlist = id.split(",");
+ if(updateElementlist.length > 1)
+ firstId = updateElementlist[0];
+ var updateElement = $(firstId);
if (updateElement == null) {
alert('There is no element on this page with the id "' + id + '".');
}
var finalUrl = AjaxSubmitButton.generateActionUrl(id, form, queryParams, options);
var finalOptions = AjaxSubmitButton.processOptions(form, options);
- new Ajax.Updater(id, finalUrl, finalOptions);
+ new Ajax.Updater(firstId, finalUrl, finalOptions);
},
request: function(form, queryParams, options) {
@@ -463,15 +484,15 @@ var AjaxSubmitButton = {
new Ajax.Request(finalUrl, finalOptions);
},
- observeDescendentFields: function(updateContainerID, containerID, observeFieldFrequency, partial, observeDelay, options) {
+ observeDescendentFields: function(updateContainerID, containerID, observeFieldFrequency, partial, observeDelay, options, actOnInput) {
$(containerID).descendants().find(function(element) {
if (element.type != 'hidden' && ['input', 'select', 'textarea'].include(element.tagName.toLowerCase())) {
- AjaxSubmitButton.observeField(updateContainerID, element, observeFieldFrequency, partial, observeDelay, options);
+ AjaxSubmitButton.observeField(updateContainerID, element, observeFieldFrequency, partial, observeDelay, options, actOnInput);
}
});
},
- observeField: function(updateContainerID, formFieldID, observeFieldFrequency, partial, observeDelay, options) {
+ observeField: function(updateContainerID, formFieldID, observeFieldFrequency, partial, observeDelay, options, actOnInput) {
var submitFunction;
if (partial) {
// We need to cheat and make the WOForm that contains the form action appear to have been
@@ -510,7 +531,7 @@ var AjaxSubmitButton = {
new Form.Element.RadioButtonObserver($(formFieldID), submitFunction);
}
else {
- new Form.Element.EventObserver($(formFieldID), submitFunction);
+ new Form.Element.ExtendedEventObserver($(formFieldID), submitFunction, actOnInput);
}
}
else {
@@ -978,6 +999,37 @@ Form.Element.RadioButtonObserver = Class.create(Form.Element.EventObserver, {
}
});
+Form.Element.ExtendedEventObserver = Class.create(Form.Element.EventObserver, {
+ initialize: function($super, element, callback, actOnInput) {
+ this.actOnInput = actOnInput;
+ $super(element, callback);
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ case 'text':
+ case 'number':
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ if (this.actOnInput)
+ {
+ Event.observe(element, 'input', this.onElementEvent.bind(this));
+ Event.observe(element, 'blur', this.onElementEvent.bind(this));
+ }
+ break;
+ default:
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+
+});
+
var AjaxBusy = {
spinners: {},