Commits

Anonymous committed 34b3d42

refactored the validation servlet client js into separate js include, with a simple oject model.
added action errors into the protocol.
doesn't work with IE due to differences in DOM implementation - or my understanding of it :)
still work in progress.. have fun

git-svn-id: http://svn.opensymphony.com/svn/webwork/trunk@664573baa09-0c28-0410-bef9-dab3c582ae83

Comments (0)

Files changed (5)

src/java/com/opensymphony/webwork/dispatcher/ServletDispatcher.java

      * @param request the HttpServletRequest object.
      * @return the name or alias of the action to execute.
      */
-    protected String getActionName(HttpServletRequest request) {
+    public static String getActionName(HttpServletRequest request) {
         String servletPath = (String) request.getAttribute("javax.servlet.include.servlet_path");
 
         if (servletPath == null) {
      * @param name the full action path.
      * @return the action name stripped of path/context info.
      */
-    protected String getActionName(String name) {
+    public static String getActionName(String name) {
         // Get action name ("Foo.action" -> "Foo" action)
         int beginIdx = name.lastIndexOf("/");
         int endIdx = name.lastIndexOf(".");

src/java/com/opensymphony/webwork/validators/ValidationServlet.java

 package com.opensymphony.webwork.validators;
 
+import com.opensymphony.util.TextUtils;
+import com.opensymphony.webwork.views.util.TextUtil;
 import com.opensymphony.xwork.*;
 import com.opensymphony.xwork.config.entities.ActionConfig;
 import org.w3c.dom.Document;
 
             PrintWriter out = res.getWriter();
             out.println("<?xml version=\"1.0\"?>");
-            out.println("<es>");
+            out.println("<errors>");
 
             if (a instanceof ValidationAware) {
                 ValidationAware va = (ValidationAware) a;
+                out.println("<actionErrors>");
+                for (Iterator iter = va.getActionErrors().iterator(); iter.hasNext(); ) {
+					String ae = (String) iter.next();
+					// TODO xmlencode the error strings
+					out.println("<errorMessage>" + ae + "</errorMessage>");
+				}
+                out.println("</actionErrors>");
+				
                 Map fe = va.getFieldErrors();
                 for (Iterator iterator = fe.entrySet().iterator(); iterator.hasNext();) {
                     Map.Entry entry = (Map.Entry) iterator.next();
                     String name = (String) entry.getKey();
                     List errors = (List) entry.getValue();
+                    out.println("<fieldErrors fieldName=\"" + name + "\">");
                     for (Iterator iterator1 = errors.iterator(); iterator1.hasNext();) {
                         String error = (String) iterator1.next();
-                        out.println("<e n=\"" + name + "\">" + error + "</e>");
+    					// TODO xmlencode the error strings and field names
+                        out.println("<errorMessage>" + error + "</errorMessage>");
                     }
+                    out.println("</fieldErrors>");
                 }
             }
 
-            out.println("</es>");
+            out.println("</errors>");
         } catch (Exception e) {
             e.printStackTrace();
         }

src/java/template/xhtml/validation.vm

+<script src="validationServlet.js"></script>
+
 <script>
-var xmlhttp=false;
 
-/*@cc_on @*/
-/*@if (@_jscript_version >= 5)
-// JScript gives us Conditional compilation, we can cope with old IE versions.
-// and security blocked creation of the objects.
- try {
-  xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");  } catch (e) {
-  try {
-   xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
-  } catch (E) {
-   xmlhttp = false;
-  }
- }
-@end @*/
+// TODO is this the best way to subclass in javascript ?
+
+var validationServlet = new ValidationServlet("$parameters.url");
+var oldOnErrors = validationServlet.onErrors;
 
-if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
-  xmlhttp = new XMLHttpRequest();
+validationServlet.onErrors = function(validateElement, errors) {
+	clearErrorRows(validateElement.parentNode.parentNode.parentNode);
+	clearErrorLabels(validateElement.form);
+	oldOnErrors.call(this, validateElement, errors);
+}
+validationServlet.onFieldError = function(validateElement, fieldName, errorMessages) {
+	if (validateElement.form.elements[fieldName].touched) {
+		for (var i = 0; i < errorMessages.length; i++) {
+			addError(validateElement.form.elements[fieldName], errorMessages[i]);
+		}
+	}
 }
 
-function validate(e) {
-    // mark the element as touch
-    e.touched = true;
-    xmlhttp.open("POST", "$parameters.url", true);
-    xmlhttp.onreadystatechange = function() {
-        if (xmlhttp.readyState == 4) {
-            var xml = xmlhttp.responseXML;
-            clearErrorRows(e.parentNode.parentNode.parentNode);
-            clearErrorLabels(e.form);
-            var errors = xml.childNodes[0].getElementsByTagName("e");
-            for (var i = 0; i < errors.length; i++) {
-                var error = errors[i];
-                var errorName = error.getAttribute("n");
-                var errorText = error.childNodes[0].textContent;
-                // there was an error, report it
-                var element = e.form.elements[errorName];
-                if (element != null && element.touched && element.touched == true) {
-                    addError(element, errorText);
-                }
-            }
-        }
-    }
 
-    var req = getValidateXml(e.form);
-    xmlhttp.send(req);
+function validate(element) {
+    // mark the element as touch
+    element.touched = true;
+	validationServlet.validate(element, element.form.namespace, element.form.name);
 }
 
 function clearErrorRows(table) {
 
 }
 
-function getValidateXml(f) {
-    var actionName = f.name;
-    var namespace = f.getAttribute("namespace");
-    var xml = '<r a="' + actionName + '" ns="' + namespace +'">\n';
-    for (var i = 0; i < f.elements.length; i++) {
-        var e = f.elements[i];
-        xml = xml + '<p n="' + e.name + '">' + e.value + '</p>\n';
-    }
-    xml = xml + '</r>\n';
-    return xml;
-}
 
 function addError(e, errorText) {
     try {
         alert(e);
     }
 }
-</script>
+
+</script>

src/webapp/javascript-input.jsp

         </ww:component>
     </head>
 
+
     <body>
-        <ww:form name="'javascriptValidation'" action="'javascriptValidation'" validate="true" >
+        <ww:form name="'javascriptValidation'" action="'javascriptValidation'" validate="true">
             <ww:textfield label="'Required String'" name="'requiredString'" />
             <ww:textfield label="'Some Int'" name="'intRange'" />
             <ww:select label="'Email (select)'" name="'email'" list="{'Select', 'foo@bar.com', 'baz@biz.com'}"/>

src/webapp/validationServlet.js

+/**
+ *
+ * Common code to interface with the validationServlet 
+ *
+ */
+
+function ValidationServlet(servletUrl) {
+
+	this.servletUrl = servletUrl;
+	this.xmlhttp=null;
+
+	// These are in order of decreasing likelihood; this will change in time.
+	XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
+
+	newXMLHttp = function() {
+
+		var xmlhttp;
+		if(typeof XMLHttpRequest !== 'undefined') {
+			xmlhttp = new XMLHttpRequest();
+		}
+		else if(typeof ActiveXObject !== 'undefined') {
+			var last_e = null;
+		
+			for(var i=0; i<XMLHTTP_PROGIDS.length; ++i) {
+				var progid = XMLHTTP_PROGIDS[i];
+				try {
+					xmlhttp = new ActiveXObject(progid);
+				}
+				catch(e) {
+					last_e = e;
+				}
+				if(this.xmlhttp) {
+					XMLHTTP_PROGIDS = [progid];  // so faster next time
+					break;
+				}
+			}
+		}
+		return xmlhttp;
+	}
+	
+	this.getValidateXml = function(form, nameSpace, actionName) {
+	    var xml = '<r a="' + actionName + '" ns="' + nameSpace +'">\n';
+	    for (var i = 0; i < form.elements.length; i++) {
+	        var e = form.elements[i];
+			// TODO get the value of the input dependant on the type of the input
+			// TODO xml escape
+	        xml = xml + '<p n="' + e.name + '">' + e.value + '</p>\n';
+	    }
+	    xml = xml + '</r>\n';
+	    return xml;
+	}
+
+	this.validate = function(input, nameSpace, actionName) {
+		var xmlhttp = newXMLHttp();
+		if (xmlhttp == null) {
+			return null;
+		}
+
+		var sv = this;
+		
+	    xmlhttp.open("POST", this.servletUrl, true);
+    	xmlhttp.onreadystatechange = function() {
+	        if (xmlhttp.readyState == 4) {
+	        	if (xmlhttp.responseXML) {
+		            sv.handleXmlResponse(input, xmlhttp.responseXML);
+				}
+	        }
+	    }
+	    
+	    var xml = this.getValidateXml(input.form, nameSpace, actionName);
+	    xmlhttp.send(xml);
+    }
+    
+	extractErrorMessages = function(node) {
+        var actionErrorMessagesXml = node.getElementsByTagName("errorMessage");
+        var ary = new Array();
+        for (var i = 0; i < actionErrorMessagesXml.length; i++) {
+        	ary[i] = actionErrorMessagesXml[i].textContent;
+        }
+        return ary;
+	}
+	
+	this.handleXmlResponse = function(input, xml) {
+
+        var root = xml.childNodes[0];
+
+        // build a javascript object to hold the errors
+        var errors = new Object();
+
+		// add the actionErrors
+        errors.actionErrors = new Array();
+
+		errors.actionErrors = extractErrorMessages(root.getElementsByTagName("actionErrors")[0]);
+
+		// add the fieldErrors
+		errors.fieldErrors = new Object();
+		var fieldErrorsXml = root.getElementsByTagName("fieldErrors");
+        for (var i = 0; i < fieldErrorsXml.length; i++) {
+        	var fieldName = fieldErrorsXml[i].getAttribute("fieldName");
+            errors.fieldErrors[fieldName] = extractErrorMessages(fieldErrorsXml[i]);
+		}
+
+        
+        // make the callback with the errors
+        this.onErrors(input, errors);
+    }
+
+	// default implementation delegates to individual on??Error handlers
+	// @param validateElement - the form element that triggered the validate(element) call
+	// @param errors - a javascript object representing the action errors and field errors
+	this.onErrors = function(validateElement, errors) {
+		this.onActionErrors(validateElement, errors);
+		for (fe in errors.fieldErrors) {
+			this.onFieldError(validateElement, fe, errors.fieldErrors[fe]);
+		}
+	}
+
+	// default implementation calls onActionError for each error message
+	// @param validateElement - the form element that triggered the validate(element) call
+	// @param errorMessages - an array of string messages
+	this.onActionErrors = function(validateElement, errorMessages) {
+		for (i = 0; i < errorMessages.length; i++) {
+			this.onActionError(validateElement, errorMessages[i]);
+		}
+	}
+
+	// default implementation does nothing
+	// @param validateElement - the form element that triggered the validate(element) call
+	// @param errorMessages
+	this.onActionError = function(validateElement, errorMessage) {
+	}
+
+	// default implementation does nothing
+	// @param validateElement - the form element that triggered the validate(element) call
+	// @param fieldName - the name of the field that the errorMessage belongs to
+	// @errorMessages - an array of string error messages
+	this.onFieldError = function(validateElement, fieldName, errorMessages) {
+	}
+	
+	return this;
+
+}