diff options
author | Manuel Carrasco <manolo@apache.org> | 2013-11-26 23:00:46 -0800 |
---|---|---|
committer | Manuel Carrasco <manolo@apache.org> | 2013-11-26 23:00:46 -0800 |
commit | eeedaf2392458bbfbd4413e99dba42bcafe3710f (patch) | |
tree | 9b70a1491281d3d33ec1e503759ff03c139db4e6 | |
parent | 2b98d91c6728422aed6ae50cb35eb6636048170f (diff) | |
parent | 4d67d2058cf7d34281f2c4cb76766839438495c6 (diff) | |
download | gwtquery-eeedaf2392458bbfbd4413e99dba42bcafe3710f.tar.gz gwtquery-eeedaf2392458bbfbd4413e99dba42bcafe3710f.zip |
Merge pull request #238 from manolo/master
done
-rw-r--r-- | gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java | 7 | ||||
-rw-r--r-- | gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/deferred/Deferred.java | 2 | ||||
-rw-r--r-- | gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java | 19 | ||||
-rw-r--r-- | gwtquery-core/src/main/java/com/google/gwt/query/rebind/JsniBundleGenerator.java | 207 | ||||
-rw-r--r-- | gwtquery-core/src/main/java/com/google/web/bindery/requestfactory/shared/gquery/PromiseRF.java (renamed from gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/deferred/PromiseRF.java) | 3 | ||||
-rw-r--r-- | gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTestGwt.java | 8 |
6 files changed, 208 insertions, 38 deletions
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java index 5950f6bf..479f90ab 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/GQuery.java @@ -25,6 +25,7 @@ import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArray; import com.google.gwt.core.client.JsArrayMixed; import com.google.gwt.core.client.JsArrayString; +import com.google.gwt.core.client.ScriptInjector; import com.google.gwt.dom.client.*; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.dom.client.Style.HasCssName; @@ -176,7 +177,7 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> { /** * The window object. */ - public static final Element window = GWT.isClient() ? window() : null; + public static final Element window = GWT.isClient() ? ScriptInjector.TOP_WINDOW.<Element>cast() : null; private static Element windowData = null; @@ -766,10 +767,6 @@ public class GQuery implements Lazy<GQuery, LazyGQuery> { e.value = value; }-*/; - private static native Element window() /*-{ - return $wnd; - }-*/; - protected Node currentContext; protected String currentSelector; diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/deferred/Deferred.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/deferred/Deferred.java index 58dcf461..82d1ff74 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/deferred/Deferred.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/deferred/Deferred.java @@ -30,7 +30,7 @@ public class Deferred implements Promise.Deferred { * Implementation of the Promise interface which is used internally by * Deferred. */ - static class DeferredPromiseImpl implements Promise { + public static class DeferredPromiseImpl implements Promise { // Using 'int' instead of 'enum' because we use them as indexes as well private static final int DONE = 0, FAIL = 1, PROGRESS = 2; diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java index eacc9d18..b8e73ae1 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java @@ -31,7 +31,6 @@ import com.google.gwt.user.client.Event; import com.google.gwt.user.client.EventListener; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** @@ -400,13 +399,15 @@ public class EventsListener implements EventListener { return ret != null ? ret : new EventsListener(e); } + /** + * We have to set the gQuery event listener to the element again when + * the element is a widget, because when GWT detaches a widget it removes the + * event listener. + */ public static void rebind(Element e) { EventsListener ret = getGQueryEventListener(e); - if (ret != null && ret.eventBits != 0) { - // rebind bit event - ret.sink(ret.eventBits, null); - - // TODO manage bitless event + if (ret != null) { + DOM.setEventListener((com.google.gwt.user.client.Element) e, ret); } } @@ -427,7 +428,11 @@ public class EventsListener implements EventListener { private static native void init(Element elem, EventsListener gqevent)/*-{ elem.__gwtlistener = @com.google.gwt.user.client.DOM::getEventListener(*)(elem); - elem.__gqueryevent = gqevent; + elem.__gqueryevent = gqevent; + // Someone has reported that in IE the init can be called multiple times + // causing a loop. We need some test to demonstrate this behaviour though. + // Anyway we check this condition to avoid looping + if (elem.__gwtlistener == gqevent) elem.__gwtlistener = null; }-*/; // Gwt does't handle submit nor resize events in DOM.sinkEvents diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/rebind/JsniBundleGenerator.java b/gwtquery-core/src/main/java/com/google/gwt/query/rebind/JsniBundleGenerator.java index 27c91d55..342909de 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/rebind/JsniBundleGenerator.java +++ b/gwtquery-core/src/main/java/com/google/gwt/query/rebind/JsniBundleGenerator.java @@ -15,11 +15,15 @@ */ package com.google.gwt.query.rebind; +import java.io.File; +import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; -import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.ByteArrayOutputStream; import com.google.gwt.core.ext.Generator; @@ -81,36 +85,21 @@ public class JsniBundleGenerator extends Generator { return null; } } - String file = packageName.replace(".", "/") + "/" + value; try { - InputStream is = this.getClass().getClassLoader().getResourceAsStream(file); - OutputStream os = new ByteArrayOutputStream(); - IOUtils.copy(is, os); - - String jsni = os.toString() - // remove MS <CR> - .replace("\r", "") - // remove 'c' (/* */) style comments blocks - .replaceAll("/\\*(?>(?:(?>[^\\*]+)|\\*(?!/))*)\\*/", "") - // remove 'c++' (//) style comment lines - .replaceAll("(?m)^\\s*//.*$", "") - // remove 'c++' (//) style comments at the end of a code line - .replaceAll("(?m)^(.*)//[^'\"]*?$", "$1") - // remove empty lines - .replaceAll("\n+", "\n"); - ; + // Read the javascript content + String content = getContent(logger, packageName.replace(".", File.separator) , value); + + // Adjust javascript so as we can introduce it in a JSNI comment block without + // breaking java syntax. + String jsni = parseJavascriptSource(content); - // Using pw instead of sw in order to avoid stack errors because sw.print is a recursive function - // and it fails with very long javascript files. - - // JMethod.toString() prints the java signature of the method, so we just have to replace abstract by native. pw.println(method.toString().replace("abstract", "native") + "/*-{"); pw.println(prepend); pw.println(jsni); pw.println(postpend); pw.println("}-*/;"); } catch (Exception e) { - e.printStackTrace(); + logger.log(TreeLogger.ERROR, "Error parsing javascript source: " + value + " "+ e.getMessage()); throw new UnableToCompleteException(); } } @@ -121,4 +110,174 @@ public class JsniBundleGenerator extends Generator { return fullName; } + + /** + * Get the content of a javascript source. It supports remote sources hosted in CDN's. + */ + private String getContent(TreeLogger logger, String path, String src) throws UnableToCompleteException { + HttpURLConnection connection = null; + InputStream in = null; + try { + if (!src.matches("(?i)https?://.*")) { + String file = path + File.separator + src; + logger.log(TreeLogger.INFO, getClass().getSimpleName() + " - importing external javascript: " + file); + in = this.getClass().getClassLoader().getResourceAsStream(file); + if (in == null) { + logger.log(TreeLogger.ERROR, "Unable to read javascript file: " + file); + } + } else { + logger.log(TreeLogger.INFO, getClass().getSimpleName() + " - downloading external javascript: " + src); + URL url = new URL(src); + connection = (HttpURLConnection) url.openConnection(); + connection.setRequestProperty("Accept-Encoding", "gzip, deflate"); + connection.setRequestProperty("Host", url.getHost()); + connection.setConnectTimeout(3000); + connection.setReadTimeout(3000); + + int status = connection.getResponseCode(); + if (status != HttpURLConnection.HTTP_OK) { + logger.log(TreeLogger.ERROR, "Server Error: " + status + " " + connection.getResponseMessage()); + throw new UnableToCompleteException(); + } + + String encoding = connection.getContentEncoding(); + in = connection.getInputStream(); + if ("gzip".equalsIgnoreCase(encoding)) { + in = new GZIPInputStream(in); + } else if ("deflate".equalsIgnoreCase(encoding)) { + in = new InflaterInputStream(in); + } + } + + return inputStreamToString(in); + } catch (IOException e) { + logger.log(TreeLogger.ERROR, "Error: " + e.getMessage()); + throw new UnableToCompleteException(); + } finally { + if (connection != null) { + connection.disconnect(); + } + } + } + + /** + * Adapt a java-script block which could produce a syntax error when + * embedding it in a JSNI block. + * + * The objective is to replace any 'c' comment-ending occurrence to avoid closing + * JSNI comment blocks prematurely. + * + * A Regexp based parser is not reliable, this approach is better and faster. + */ + // Note: this comment is intentionally using c++ style to allow writing the '*/' sequence. + // + // - Remove C comments: /* ... */ + // - Remove C++ comments: // ... + // - Escape certain strings: '...*/...' to '...*' + '/...' + // - Rewrite inline regex: /...*/igm to new RegExp('...*' + 'igm'); + private String parseJavascriptSource(String js) throws Exception { + + boolean isJS = true; + boolean isSingQuot = false; + boolean isDblQuot = false; + boolean isSlash = false; + boolean isCComment=false; + boolean isCPPComment=false; + boolean isRegex = false; + boolean isOper = false; + + StringBuilder ret = new StringBuilder(); + String tmp = ""; + Character last = 0; + Character prev = 0; + + for (int i = 0, l = js.length(); i < l ; i++) { + Character c = js.charAt(i); + String out = c.toString(); + + if (isJS) { + isDblQuot = c == '"'; + isSingQuot = c == '\''; + isSlash = c == '/'; + isJS = !isDblQuot && !isSingQuot && !isSlash; + if (!isJS) { + out = tmp = ""; + isCPPComment = isCComment = isRegex = false; + } + } else if (isSingQuot) { + isJS = !(isSingQuot = last == '\\' || c != '\''); + if (isJS) out = escapeQuotedString(tmp, c); + else tmp += c; + } else if (isDblQuot) { + isJS = !(isDblQuot = last == '\\' || c != '"'); + if (isJS) out = escapeQuotedString(tmp, c); + else tmp += c; + } else if (isSlash) { + if (!isCPPComment && !isCComment && !isRegex && !isOper) { + isCPPComment = c == '/'; + isCComment = c == '*'; + isOper = !isCPPComment && !isCComment && !"=(&|?:;},".contains(""+prev); + isRegex = !isCPPComment && !isCComment && !isOper; + } + if (isOper) { + isJS = !(isSlash = isOper = false); + out = "" + last + c; + } else if (isCPPComment) { + isJS = !(isSlash = isCPPComment = c != '\n'); + if (isJS) out = "\n"; + } else if (isCComment) { + isSlash = isCComment = !(isJS = (last == '*' && c == '/')); + if (isJS) out = ""; + } else if (isRegex) { + isJS = !(isSlash = isRegex = (last == '\\' || c != '/')); + if (isJS) { + String mod = ""; + while (++i < l) { + c = js.charAt(i); + if ("igm".contains(""+c)) mod += c; + else break; + } + out = escapeInlineRegex(tmp, mod) + c; + } else { + tmp += c; + } + } else { + isJS = true; + } + } + + if (isJS) { + ret.append(out); + } + if (last != ' ') { + prev = last; + } + last = prev == '\\' && c == '\\' ? 0 : c; + } + return ret.toString(); + } + + private String escapeQuotedString(String s, Character quote) { + return quote + s.replace("*/", "*" + quote + " + " + quote + "/") + quote; + } + + private String escapeInlineRegex(String s, String mod) { + if (s.endsWith("*")) { + return "new RegExp('" + s.replace("\\", "\\\\") + "','" + mod + "')"; + } else { + return '/' + s + '/' + mod; + } + } + + private String inputStreamToString(InputStream in) throws IOException { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int read = in.read(buffer); + while (read != -1) { + bytes.write(buffer, 0, read); + read = in.read(buffer); + } + in.close(); + return bytes.toString(); + } } diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/deferred/PromiseRF.java b/gwtquery-core/src/main/java/com/google/web/bindery/requestfactory/shared/gquery/PromiseRF.java index 5f573a1d..76be5b89 100644 --- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/deferred/PromiseRF.java +++ b/gwtquery-core/src/main/java/com/google/web/bindery/requestfactory/shared/gquery/PromiseRF.java @@ -11,7 +11,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.query.client.plugins.deferred; +package com.google.web.bindery.requestfactory.shared.gquery; import java.util.ArrayList; import java.util.List; @@ -19,6 +19,7 @@ import java.util.Set; import javax.validation.ConstraintViolation; +import com.google.gwt.query.client.plugins.deferred.Deferred; import com.google.gwt.query.client.plugins.deferred.Deferred.DeferredPromiseImpl; import com.google.web.bindery.requestfactory.shared.Receiver; import com.google.web.bindery.requestfactory.shared.Request; diff --git a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTestGwt.java b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTestGwt.java index b13b3b4e..6795a35d 100644 --- a/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTestGwt.java +++ b/gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEventsTestGwt.java @@ -1288,6 +1288,7 @@ public class GQueryEventsTestGwt extends GWTTestCase { b.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { b.getElement().getStyle().setBackgroundColor("black"); + b.setWidth("100px"); } }); RootPanel.get().add(b); @@ -1296,6 +1297,9 @@ public class GQueryEventsTestGwt extends GWTTestCase { $(b).click(); assertEquals("red", $("button").css("color", false)); assertEquals("black", $("button").css("background-color", false)); + assertEquals("100px", $("button").css("width", false)); + + $(b).bind("custom", lazy().css("width", "200px").done()); RootPanel.get().remove(b); $(e).append($(b)); @@ -1303,6 +1307,10 @@ public class GQueryEventsTestGwt extends GWTTestCase { $(b).click(); assertEquals("red", $("button").css("color", false)); assertEquals("black", $("button").css("background-color", false)); + assertEquals("100px", $("button").css("width", false)); + + $(b).trigger("custom"); + assertEquals("200px", $("button").css("width", false)); } public void testIssue152() { |