public void makeBatchRequests() { if (requestQueue.size() > 0) { List<AjaxRequest> queue = requestQueue; requestQueue = StrictWeb.jsNewList(); makeRequest(true, queue, null); } autoRequest = true; }
public Object call( String clazz, final String method, Object[] args, final VoidDelegate<Object> callBack, final VoidDelegate<Throwable> errorHandler) throws Throwable { if (errorHandler != null) errorHandler.voidDelegate(null); else onError(null); boolean async = callBack != null; if (!async || autoRequest) { List<AjaxRequest> requests = StrictWeb.jsNewList(); requests.add(new AjaxRequest(clazz, method, args, callBack, errorHandler)); return makeRequest(async, requests, errorHandler); } else { requestQueue.add(new AjaxRequest(clazz, method, args, callBack, errorHandler)); return null; } }
public class Ajax { public static final String FIELD_MULTISELECT = "field.multiselect"; private static final String MICROSOFT_XMLHTTP = "Microsoft.XMLHTTP"; private static final String EV_ONREADYSTATECHANGE = "onreadystatechange"; public static Ajax helper; boolean autoRequest = true; List<AjaxRequest> requestQueue = StrictWeb.jsNewList(); public String getRequestUrl(List<AjaxRequest> requests) { return "/ajax"; } // public String getQueryString(List<AjaxRequest> requests) { // String query = "_rnd="+Math.random(); // // for(int i=0; i<requests.size(); i++) { // AjaxRequest r = requests.get(i); // query += "&" + BEAN_NAME_PARAM+i+"="+r.clazz + "&"+METHOD_NAME_PARAM+i+"="+r.method; // } // // return query; // } public String getLoadingImageUrl() { return null; } public XMLHttpRequest getHttpRequest() { if (StrictWeb.window.XMLHttpRequest != null) { return new XMLHttpRequest(); } return (XMLHttpRequest) (Object) new ActiveXObject(MICROSOFT_XMLHTTP); } /** this method handles error events. If null is passed - this means reset errors */ public void onError(Throwable exception) { if (exception == null) return; Log.error("Ajax call exception: " + exception.getMessage()); if (exception.getStackTrace() != null) for (StackTraceElement ste : exception.getStackTrace()) { Log.error( " at " + ste.getClassName() + "." + ste.getMethodName() + " : " + ste.getLineNumber()); } if (exception.getCause() != null) { Log.error("Caused by:"); onError(exception.getCause()); } } public void beginBatch() { autoRequest = false; } public void makeBatchRequests() { if (requestQueue.size() > 0) { List<AjaxRequest> queue = requestQueue; requestQueue = StrictWeb.jsNewList(); makeRequest(true, queue, null); } autoRequest = true; } public Object call( String clazz, final String method, Object[] args, final VoidDelegate<Object> callBack, final VoidDelegate<Throwable> errorHandler) throws Throwable { if (errorHandler != null) errorHandler.voidDelegate(null); else onError(null); boolean async = callBack != null; if (!async || autoRequest) { List<AjaxRequest> requests = StrictWeb.jsNewList(); requests.add(new AjaxRequest(clazz, method, args, callBack, errorHandler)); return makeRequest(async, requests, errorHandler); } else { requestQueue.add(new AjaxRequest(clazz, method, args, callBack, errorHandler)); return null; } } Object makeRequest( boolean async, final List<AjaxRequest> requests, final VoidDelegate<Throwable> errorHandler) { onError(null); XMLHttpRequest request = getHttpRequest(); // String postXml = ""; // // for(int i=0; i<requests.size(); i++) { // AjaxRequest r = requests.get(i); // if(r.errorHandler != null) r.errorHandler.voidDelegate(null); // postXml += "&"+XML_DATA_PARAM+i+"=" // + objectToXml(r.args, null).replaceAll("%", "%25").replaceAll("&", // "%26").replaceAll(";", "%3B").replaceAll("\\+", "%2B"); // } String postJson = "["; for (int i = 0; i < requests.size(); i++) { AjaxRequest r = requests.get(i); postJson += objectToJson(r.clazz) + "," + objectToJson(r.method) + "," + arrayToJson(r.args); if (i != requests.size() - 1) postJson += ","; } postJson += "]"; final String url = getRequestUrl(requests); final Node[] eventTargetNodes = new Node[3]; eventTargetDisable(eventTargetNodes); if (async) { StrictWeb.setVoidEventCallback( request, EV_ONREADYSTATECHANGE, new VoidDelegate<XMLHttpRequest>() { public void voidDelegate(XMLHttpRequest request) { if (request.readyState == 4) { try { StrictWeb.setVoidEventCallback(request, EV_ONREADYSTATECHANGE, null); } catch (Exception e) { /*StrictWeb.window.alert(e);*/ } eventTargetEnable(eventTargetNodes); if (request.status == 200) { AjaxRequestResult[] results = parseRequestResult(request, url, requests); for (int i = 0; i < results.length; i++) { AjaxRequestResult res = results[i]; AjaxRequest req = requests.get(i); Throwable error = res.getError(); if (error != null) { throwError( "Ajax.call(" + req.method + "): server-side exception", error, req.errorHandler); } else { req.callBack.voidDelegate(res.data); } } } else { throwError("Error status: " + request.status, null, errorHandler); } } } }); } // postXml = getQueryString(requests) + postXml; request.open("POST", url, async, null, null); Log.info("POST URL : " + url); Log.info("POST JSON: " + postJson); Log.info("POST args:"); Log.info(requests); setRequestHeaders(request); request.send(postJson); if (async) return null; // doing sync parse eventTargetEnable(eventTargetNodes); AjaxRequestResult res = parseRequestResult(request, url, requests)[0]; AjaxRequest req = requests.get(0); Throwable error = res.getError(); if (error != null) { throwError("Ajax.call(" + req.method + "): server-side exception", error, req.errorHandler); } return res.data; } public void setRequestHeaders(XMLHttpRequest request) { // request.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); request.setRequestHeader("Content-type", "application/json"); } private AjaxRequestResult[] parseRequestResult( XMLHttpRequest request, String url, List<AjaxRequest> requests) { long status = request.status; String responseText = request.responseText; String statusText = request.statusText; request = null; AjaxRequestResult[] result = null; if (status == 200) { try { Object evalResult; if (responseText.charAt(0) == 'v') { evalResult = StrictWeb.evalFunction(responseText + "\nreturn o._a;"); } else { evalResult = StrictWeb.evalFunction("return " + responseText); } result = (AjaxRequestResult[]) evalResult; Log.debug("Ajax result:"); Log.debug(result); } catch (Throwable e) { throwError("Ajax.call(" + getMethodsNames(requests) + "): eval error", e, null); } } else { throwError( "Ajax.call(" + getMethodsNames(requests) + "): http error:\n" + "URL: " + url + "\nCode: " + status + "\nMessage: " + statusText, null, null); } if (result == null) { throwError( "Ajax.call(" + getMethodsNames(requests) + "): result is null: " + responseText, null, null); } if (result.length != requests.size()) { throwError( "Ajax.call(" + getMethodsNames(requests) + "): results count in not equal requests count: " + result.length + "!=" + requests.size(), null, null); } return result; } public String getMethodsNames(List<AjaxRequest> requests) { String methods = ""; for (int i = 0; i < requests.size(); i++) { AjaxRequest r = requests.get(i); methods += (i > 0 ? "," : "") + r.method; } return methods; } public void eventTargetDisable(Node[] eventTargetNodes) { DOMEvent ev = StrictWeb.globalEvent; Node el = StrictWeb.swTarget; if (el == null) { if (null == ev) return; el = ev.target; } try { if (el == null) el = ev.srcElement; if (el == null) el = ev.fromElement; } catch (Exception e) { /* NOP */ } if (el == null || null == el.tagName || el.parentNode == null) return; // node eventTargetNodes[0] = el; // parent eventTargetNodes[1] = el.parentNode; // loading String loadingImgUrl = getLoadingImageUrl(); eventTargetNodes[2] = (null != loadingImgUrl ? StrictWeb.EL("span") .styleDisplay("inline-block") .styleWidth(el.offsetWidth + "px") .styleHeight(el.offsetHeight + "px") .styleBackground("center center no-repeat url(" + loadingImgUrl + ")") : StrictWeb.EL("span").text("loading...")) .node; eventTargetNodes[1].insertBefore(eventTargetNodes[2], eventTargetNodes[0]); eventTargetNodes[1].removeChild(eventTargetNodes[0]); } public void eventTargetEnable(Node[] eventTargetNodes) { if (null == eventTargetNodes || eventTargetNodes.length != 3) return; eventTargetNodes[1].insertBefore(eventTargetNodes[0], eventTargetNodes[2]); eventTargetNodes[1].removeChild(eventTargetNodes[2]); eventTargetNodes[0] = eventTargetNodes[1] = eventTargetNodes[2] = null; } void throwError(String shortMsg, Throwable th, VoidDelegate<Throwable> errorHandler) { if (th == null) th = new JsException(shortMsg); if (errorHandler != null) { errorHandler.voidDelegate(th); } else { onError(th); } } @NativeCode("{throw th;}") private static <T> T nativeJsThrow(Object th) { return null; } @MayBeExcluded public static String objectToJson(Object obj) { if (StrictWeb.jsTypeOf(obj) == "undefined") return "null"; if (StrictWeb.jsTypeOf(obj) == "string") return '"' + StrictWeb.toJSON((String) obj) + '"'; if (StrictWeb.jsTypeOf(obj) == "boolean") return ((Boolean) obj) ? "1" : "0"; if (StrictWeb.jsTypeOf(obj) == "number") return obj.toString(); if (StrictWeb.jsTypeOf(obj) == "object") { if (obj == null) return "null"; if (StrictWeb.isEnum(obj)) return obj.toString(); if (StrictWeb.isInstanceOfDate(obj)) return StrictWeb.dateToStringSmart((Date) obj); if (StrictWeb.isInstanceOfArray(obj)) return arrayToJson((Object[]) obj); if (StrictWeb.isInstanceOfNode(obj)) { String json = formToJson((Node) obj); if (json != null && json.length() > 0 && json.charAt(0) == '{') return "[" + json + "]"; return "{" + json + "}"; } Map<String, String> map = (Map<String, String>) obj; String json = ""; for (String key : map.keySet()) { String val = map.get(key); if (StrictWeb.jsTypeOf(val) == "function") continue; if (json.length() > 0) json += ","; json += objectToJson(key) + ":" + objectToJson(val); } return "{" + json + "}"; } return nativeJsThrow("Unknown object type: " + StrictWeb.jsTypeOf(obj)); } @MayBeExcluded public static String arrayToJson(Object[] a) { if (a == null) return "null"; String json = ""; for (int i = 0; i < a.length; i++) { Object val = a[i]; if (StrictWeb.jsTypeOf(val) == "function") continue; if (json.length() > 0) json += ","; json += objectToJson(val); } return "[" + json + "]"; } @MayBeExcluded public static String formToJson(Node start) { if (start.field == DOMBuilder.DISABLED) return ""; String json = ""; boolean asArray = false; for (Node el : start.childNodes) { if (el.field == DOMBuilder.DISABLED) continue; if (((Boolean) (Object) el.id || (Boolean) (Object) el.name) && (el.tagName == "INPUT" || el.tagName == "SELECT" || el.tagName == "TEXTAREA")) { if (el.type == "radio" && !el.checked) continue; if (json.length() > 0) json += ","; if (asArray) throw new RuntimeException("Form returned as array, not at map"); json += objectToJson((Boolean) (Object) el.id || (Boolean) (Object) el.name) + ":"; if (el.type == "checkbox") json += el.checked ? "1" : "0"; else json += objectToJson(el.value); } else if (el.className != null && el.className.indexOf(FIELD_MULTISELECT) >= 0) { final List<Object> val = new ArrayList<Object>(); NodeBuilder.wrap(el) .forEachSubchild( new CommonDelegate<Boolean, Node>() { public Boolean delegate(Node n) { if (n.field == DOMBuilder.DISABLED) return false; if (n.tagName == "INPUT" || n.tagName == "SELECT") { if (n.type == "checkbox") { if (n.checked) val.add((Boolean) (Object) n.id || (Boolean) (Object) n.name); } else { val.add((String) n.value); } } return true; } }); if (json.length() > 0) json += ","; if (asArray) nativeJsThrow("Form returned as array, not as map 1"); json += objectToJson(el.field) + ":" + arrayToJson((Object[]) (Object) val); } else { String childJson = formToJson(el); if (childJson != null && childJson.length() > 0) { if (childJson.charAt(0) == '{') asArray = true; else if (asArray) nativeJsThrow("Form returned as array, not as map 2"); if (json.length() > 0) json += ","; json += childJson; } } } if (start.field != null) { String bra = asArray ? "[" : "{"; String ket = asArray ? "]" : "}"; if (StrictWeb.jsTypeOf(start.field) == "string") { return objectToJson(start.field) + ":" + bra + json + ket; } else return bra + json + ket; } return json; } @MayBeExcluded public static String objectToXml(Object obj, String _id) { String id = (_id != null ? " id=\"" + _id + "\"" : ""); if (StrictWeb.jsTypeOf(obj) == "undefined") return "<null" + id + "/>"; if (StrictWeb.jsTypeOf(obj) == "string") return "<s" + id + ">" + StrictWeb.toHTML((String) obj) + "</s>"; if (StrictWeb.jsTypeOf(obj) == "boolean") return ((Boolean) obj) ? "<b" + id + ">1</b>" : "<b" + id + ">0</b>"; if (StrictWeb.jsTypeOf(obj) == "number") return "<n" + id + ">" + obj + "</n>"; if (StrictWeb.jsTypeOf(obj) == "object") { if (obj == null) return "<null" + id + "/>"; if (StrictWeb.isEnum(obj)) return "<e" + id + ">" + obj.toString() + "</e>"; if (StrictWeb.isInstanceOfDate(obj)) return "<d" + id + ">" + StrictWeb.dateToStringSmart((Date) obj) + "</d>"; if (StrictWeb.isInstanceOfArray(obj)) return arrayToXml((Object[]) obj, id); if (StrictWeb.isInstanceOfNode(obj)) { return "<form" + id + ">" + formToXml((Node) obj) + "</form>"; } Map<String, String> map = (Map<String, String>) obj; String xml = "<o" + id + ">"; for (String key : map.keySet()) { String val = map.get(key); if (StrictWeb.jsTypeOf(val) != "function") xml += objectToXml(val, key); } return xml + "</o>"; } return "<" + StrictWeb.jsTypeOf(obj) + "/>"; } @MayBeExcluded public static String formToXml(Node start) { String xml = ""; if (start.field == DOMBuilder.DISABLED) return xml; if (start.field != null) { if (StrictWeb.jsTypeOf(start.field) == "string") { xml = "<f id=\"" + start.field + "\">"; } else xml = "<f>"; } for (Node el : start.childNodes) { if (el.field == DOMBuilder.DISABLED) continue; if (((Boolean) (Object) el.id || (Boolean) (Object) el.name) && (el.tagName == "INPUT" || el.tagName == "SELECT" || el.tagName == "TEXTAREA")) { if (el.type == "radio" && !el.checked) continue; xml += "<f id=\"" + ((Boolean) (Object) el.id || (Boolean) (Object) el.name) + "\">"; if (el.type == "checkbox") xml += el.checked ? "1" : "0"; else xml += StrictWeb.toHTML((String) el.value); xml += "</f>"; } else if (el.className != null && el.className.indexOf(FIELD_MULTISELECT) >= 0) { final List<Object> val = new ArrayList<Object>(); NodeBuilder.wrap(el) .forEachSubchild( new CommonDelegate<Boolean, Node>() { public Boolean delegate(Node n) { if (n.field == DOMBuilder.DISABLED) return false; if (n.tagName == "INPUT" || n.tagName == "SELECT") { if (n.type == "checkbox") { if (n.checked) val.add((Boolean) (Object) n.id || (Boolean) (Object) n.name); } else { val.add((String) n.value); } } return true; } }); xml += "<ms id=\"" + el.field + "\">" + (val.size() > 0 ? "<q>" : "") + StrictWeb.jsJoinList(val, "</q><q>") + (val.size() > 0 ? "</q>" : "") + "</ms>"; } else xml += formToXml(el); } if (start.field != null) xml += "</f>"; return xml; } @MayBeExcluded public static String arrayToXml(Object[] a, String id) { String xml = "<a" + id + ">"; for (int i = 0; i < a.length; i++) { if (StrictWeb.jsTypeOf(a[i]) != "function") xml += objectToXml(a[i], null); } return xml + "</a>"; } /** Use this method to append all Ajax specific classes to StrictWeb compier */ @Native public static void prepareCompiler(Compiler compiler) throws Exception { compiler .addPlugin(new EntityCompilerPlugin()) .addPlugin(new AjaxServiceHelperCompilerPlugin()) // .parseClass(AjaxException.class) .parseClass(AjaxRequest.class) .parseClass(AjaxRequestResult.class) .parseClass(ActiveXObject.class) .parseClass(Log.class) .parseClass(XMLHttpRequest.class) .parseClass(Ajax.class); } }