aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjdramaix <julien.dramaix@gmail.com>2013-11-22 11:39:18 +0100
committerjdramaix <julien.dramaix@gmail.com>2013-11-22 11:39:18 +0100
commit6c9ec1f3609291d27b9c287a3fe788cb8ac71916 (patch)
tree713084bc189e2916ccef1e248864454b26b1d561
parent2e783da49474361b45c7c0bbb952502649f52c83 (diff)
parent74edc0e4d202ec31bc591f19b63f54523f4ea776 (diff)
downloadgwtquery-6c9ec1f3609291d27b9c287a3fe788cb8ac71916.tar.gz
gwtquery-6c9ec1f3609291d27b9c287a3fe788cb8ac71916.zip
Merge branch 'master' of github.com:gwtquery/gwtquery into jd_event
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/Query.gwt.xml5
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/QueryMin.gwt.xml5
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/client/builders/JsniBundle.java125
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsUtils.java4
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/ajax/Ajax.java26
-rw-r--r--gwtquery-core/src/main/java/com/google/gwt/query/rebind/JsniBundleGenerator.java124
6 files changed, 286 insertions, 3 deletions
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/Query.gwt.xml b/gwtquery-core/src/main/java/com/google/gwt/query/Query.gwt.xml
index 873e09ab..a902fd99 100644
--- a/gwtquery-core/src/main/java/com/google/gwt/query/Query.gwt.xml
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/Query.gwt.xml
@@ -22,6 +22,11 @@
<when-type-assignable class="com.google.gwt.query.client.Browser"/>
</generate-with>
+ <!-- JSNI Generator -->
+ <generate-with class="com.google.gwt.query.rebind.JsniBundleGenerator">
+ <when-type-assignable class="com.google.gwt.query.client.builders.JsniBundle"/>
+ </generate-with>
+
<!-- Json and Xml builders -->
<generate-with class="com.google.gwt.query.rebind.JsonBuilderGenerator">
<when-type-assignable class="com.google.gwt.query.client.builders.JsonBuilder"/>
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/QueryMin.gwt.xml b/gwtquery-core/src/main/java/com/google/gwt/query/QueryMin.gwt.xml
index 385adde8..c67ec2af 100644
--- a/gwtquery-core/src/main/java/com/google/gwt/query/QueryMin.gwt.xml
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/QueryMin.gwt.xml
@@ -42,6 +42,11 @@
</any>
</set-property>
+ <!-- JSNI Generator -->
+ <generate-with class="com.google.gwt.query.rebind.JsniBundleGenerator">
+ <when-type-assignable class="com.google.gwt.query.client.builders.JsniBundle"/>
+ </generate-with>
+
<!-- Browser flags -->
<generate-with class="com.google.gwt.query.rebind.BrowserGenerator">
<when-type-assignable class="com.google.gwt.query.client.Browser"/>
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/builders/JsniBundle.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/builders/JsniBundle.java
new file mode 100644
index 00000000..14b3f1f2
--- /dev/null
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/builders/JsniBundle.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2013, The gwtquery team.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.query.client.builders;
+
+import static java.lang.annotation.ElementType.*;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A tag interface that is used in the generation of jsni bundles.
+ *
+ * A jsni-bundle is a class with jsni methods whose content is taken from
+ * external handwritten javascript files.
+ *
+ * The goals of using this technique are:
+ * - Use pure javascript files so as we can use IDEs for editing, formating etc,
+ * instead of dealing with code in comment blocks.
+ * - Facilitate writing and testing javascript in the browser before compiling it.
+ * - Include third-party javascript libraries without modification of the original source.
+ * - Not need of adding javascript tags in the html page or module file to include
+ * third-party javascript.
+ * - GWT compiler will get rid of any jsni fragment if the application does not use it.
+ * - Included javascript will take advantage of GWT jsni validators, obfuscators
+ * and optimizers.
+ *
+ * In summary, this mechanism facilitates the creation of GWT libraries wrapping or using
+ * external javascript code, hence the developer does not have to take care of tags,
+ * and leaving gwt compiler the decission to include external code when it is actually
+ * required.
+ *
+ * <pre>
+ public interface HighCharts extends JsniBundle {
+ @LibrarySource("highcharts.js")
+ public void initialize();
+
+ @MethodSource("createChart.js")
+ public JavaScriptObject createChart(String elementId, JavaScriptObject config);
+ }
+
+ // Generate the Bundle implementation
+ HighCharts library = GWT.create(HighCharts.class);
+
+ // Initialize the third-party library
+ library.initialize();
+
+ // Create a new chart
+ JavaScriptObject chart = hc.createChart("container", properties);
+
+ * </pre>
+ */
+public interface JsniBundle {
+
+ /**
+ * Annotation to mark inclusion of third-party libraries.
+ *
+ * The content is wrapped with an inner javascript function to set
+ * the context to the document window instead of the iframe where GWT
+ * code is run.
+ */
+ @Target({METHOD})
+ @Retention(RetentionPolicy.RUNTIME)
+ @Documented
+ public @interface LibrarySource {
+ /**
+ * The external file to be included
+ */
+ String value();
+
+ /**
+ * Fragment of code to include before the external javascript is
+ * written.
+ */
+ String prepend() default "(function(window, document, console){\n";
+
+ /**
+ * Fragment of code to include after the external javascript has been
+ * written.
+ */
+ String postpend() default "\n}.apply($wnd, [$wnd, $doc, $wnd.console]));";
+ }
+
+ /**
+ * Annotation to mark inclusion of jsni code writen in external js files.
+ *
+ * The content is not wrapped by default, so the developer has the responsibility
+ * to return the suitable value and to handle correctly parameters.
+ */
+ @Target({METHOD})
+ @Retention(RetentionPolicy.RUNTIME)
+ @Documented
+ public @interface MethodSource {
+ /**
+ * The external file to be included
+ */
+ String value();
+
+ /**
+ * Fragment of code to include before the external javascript is
+ * written.
+ */
+ String prepend() default "";
+
+ /**
+ * Fragment of code to include after the external javascript has been
+ * written.
+ */
+ String postpend() default "";
+ }
+}
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsUtils.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsUtils.java
index 89b1ab57..9790b12f 100644
--- a/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsUtils.java
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsUtils.java
@@ -26,6 +26,7 @@ import com.google.gwt.dom.client.NodeList;
import com.google.gwt.query.client.Function;
import com.google.gwt.query.client.GQuery;
import com.google.gwt.query.client.Properties;
+import com.google.gwt.query.client.plugins.ajax.Ajax;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
@@ -371,7 +372,10 @@ public class JsUtils {
/**
* Load an external javascript library. The inserted script replaces the
* element with the given id in the document.
+ *
+ * @deprecated use {@link com.google.gwt.query.client.plugins.ajax.Ajax#loadScript(String)}
*/
+ @Deprecated
public static void loadScript(String url, String id) {
GQuery gs = GQuery.$(DOM.createElement("script"));
GQuery gp = GQuery.$("#" + id).parent();
diff --git a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/ajax/Ajax.java b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/ajax/Ajax.java
index d50d7470..3c834330 100644
--- a/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/ajax/Ajax.java
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/ajax/Ajax.java
@@ -128,7 +128,7 @@ public class Ajax extends GQuery {
if ("jsonp".equalsIgnoreCase(dataType)) {
ret = new PromiseReqBuilderJSONP(url, null, settings.getTimeout());
- } else if ("script".equalsIgnoreCase(dataType)){
+ } else if ("loadscript".equalsIgnoreCase(dataType)){
ret = createPromiseScriptInjector(url);
} else {
ret = createPromiseRequestBuilder(settings, httpMethod, url, data)
@@ -144,6 +144,9 @@ public class Ajax extends GQuery {
retData = JsUtils.parseJSON(response.getText());
} else {
retData = response.getText();
+ if ("script".equalsIgnoreCase(dataType)) {
+ ScriptInjector.fromString((String)retData).setWindow(window).inject();
+ }
}
} catch (Exception e) {
if (GWT.getUncaughtExceptionHandler() != null) {
@@ -212,7 +215,8 @@ public class Ajax extends GQuery {
private static Promise createPromiseScriptInjector(final String url) {
return new PromiseFunction() {
public void f(final Deferred dfd) {
- ScriptInjector.fromUrl(url).setCallback(new Callback<Void, Exception>() {
+ ScriptInjector.fromUrl(url).setWindow(window)
+ .setCallback(new Callback<Void, Exception>() {
public void onSuccess(Void result) {
dfd.resolve();
}
@@ -349,7 +353,7 @@ public class Ajax extends GQuery {
}
/**
- * Load a JavaScript file from the server using a GET HTTP request, then execute it.
+ * Get a JavaScript file from the server using a GET HTTP request, then execute it.
*/
public static Promise getScript(String url) {
return getScript(url, null);
@@ -364,6 +368,22 @@ public class Ajax extends GQuery {
);
}
+ /**
+ * Load a JavaScript file from any url using the script tag mechanism
+ */
+ public static Promise loadScript(String url) {
+ return loadScript(url, null);
+ }
+
+ public static Promise loadScript(final String url, Function success) {
+ return ajax(createSettings()
+ .setUrl(url)
+ .setType("get")
+ .setDataType("loadscript")
+ .setSuccess(success)
+ );
+ }
+
public static Promise post(String url, Properties data, final Function onSuccess) {
Settings s = createSettings();
s.setUrl(url);
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
new file mode 100644
index 00000000..27c91d55
--- /dev/null
+++ b/gwtquery-core/src/main/java/com/google/gwt/query/rebind/JsniBundleGenerator.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2013, The gwtquery team.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.query.rebind;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.output.ByteArrayOutputStream;
+
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.query.client.builders.JsniBundle;
+import com.google.gwt.query.client.builders.JsniBundle.LibrarySource;
+import com.google.gwt.query.client.builders.JsniBundle.MethodSource;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+
+/**
+ * Generates an implementation of a user-defined interface <code>T</code> that
+ * extends {@link JsniBundle}.
+ *
+ * The generated implementation includes hand-written external js-files into
+ * jsni methods so as those files can take advantage of gwt compiler optimizations.
+ *
+ */
+public class JsniBundleGenerator extends Generator {
+
+ public String generate(TreeLogger logger, GeneratorContext context, String requestedClass)
+ throws UnableToCompleteException {
+
+ TypeOracle oracle = context.getTypeOracle();
+ JClassType clazz = oracle.findType(requestedClass);
+
+ String packageName = clazz.getPackage().getName();
+ String className = clazz.getName().replace('.', '_') + "_Impl";
+ String fullName = packageName + "." + className;
+
+ PrintWriter pw = context.tryCreate(logger, packageName, className);
+
+ if (pw != null) {
+
+ ClassSourceFileComposerFactory fact = new ClassSourceFileComposerFactory(packageName, className);
+ fact.addImplementedInterface(requestedClass);
+ SourceWriter sw = fact.createSourceWriter(context, pw);
+
+ if (sw != null) {
+ for (JMethod method : clazz.getMethods()) {
+ LibrarySource librarySource = method.getAnnotation(LibrarySource.class);
+ String value, prepend, postpend;
+ if (librarySource != null) {
+ value = librarySource.value();
+ prepend = librarySource.prepend();
+ postpend = librarySource.postpend();
+ } else {
+ MethodSource methodSource = method.getAnnotation(MethodSource.class);
+ if (methodSource != null) {
+ value = methodSource.value();
+ prepend = methodSource.prepend();
+ postpend = methodSource.postpend();
+ } else {
+ 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");
+ ;
+
+ // 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();
+ throw new UnableToCompleteException();
+ }
+ }
+
+ sw.commit(logger);
+ }
+ }
+
+ return fullName;
+ }
+}