aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManuel Carrasco <manolo@apache.org>2013-11-26 23:00:46 -0800
committerManuel Carrasco <manolo@apache.org>2013-11-26 23:00:46 -0800
commiteeedaf2392458bbfbd4413e99dba42bcafe3710f (patch)
tree9b70a1491281d3d33ec1e503759ff03c139db4e6
parent2b98d91c6728422aed6ae50cb35eb6636048170f (diff)
parent4d67d2058cf7d34281f2c4cb76766839438495c6 (diff)
downloadgwtquery-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.java7
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/deferred/Deferred.java2
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java19
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/rebind/JsniBundleGenerator.java207
-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.java8
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() {