GWT and SVG, the 125th... :-)

375 views
Skip to first unread message

Magnus

unread,
Feb 25, 2014, 8:31:49 AM2/25/14
to google-we...@googlegroups.com
Hi,
 
I have been searching for SVG support in GWT for an long time.
Now I have found something that seems to be exactly what I am looking for:
 
  • It's lightweight.
  • It supports importing SVG files.
  • The demo shows all the features I need (simple drawings, svg import, events)
  • It seems to support the most important browsers.
The only thing is that it's a raw JS-library.
How would you integrate it into a GWT project?
 
Consider the following (JS`?) code:
 
var draw = SVG('drawing').size(300, 300)
var rect = draw.rect(100, 100).attr({ fill: '#f06' })
How could you encapsulate this into a java method called drawRect?
 
Do you know this library and what do you think?
 
Thanks
Magnus

Alain Ekambi

unread,
Feb 25, 2014, 8:43:31 AM2/25/14
to google-we...@googlegroups.com
Also have a look at http://snapsvg.io/



--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-tool...@googlegroups.com.
To post to this group, send email to google-we...@googlegroups.com.
Visit this group at http://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/groups/opt_out.

Jens

unread,
Feb 25, 2014, 9:20:45 AM2/25/14
to google-we...@googlegroups.com
Also have a look at http://snapsvg.io/

Oh that looks pretty cool. Worth a bookmark :)

-- J.

Magnus

unread,
Feb 25, 2014, 11:45:10 AM2/25/14
to google-we...@googlegroups.com
Yes, it's cool! :-)

But what about my question? :-)

How to embed JS within GWT?

Thanks
Magnus

mark.e...@gmail.com

unread,
Feb 25, 2014, 11:48:32 AM2/25/14
to google-we...@googlegroups.com
There's two general approaches:

1) Write large JSNI methods that do all the interaction in Javascript
2) Use GWT's JavaScriptObject, subclass the library's objects, and write small JSNI methods that wrap individual methods of the JS object.

There's supposed to be better JS interop coming in GWT 3.0, but that's a ways off.

There's a number of GWT/JS wrappers out there that you could use as examples (Google Maps and Google Earth come to mind).

Jens

unread,
Feb 25, 2014, 12:17:45 PM2/25/14
to google-we...@googlegroups.com
Should cover the most: http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html

Personally I use ClientBundle + (External)TextResource + ScriptInjector to inject the JS code into the browser. 

-- J.

Magnus

unread,
Feb 25, 2014, 1:14:37 PM2/25/14
to google-we...@googlegroups.com
Thanks, it covers most of the stuff relating to embed JS within Java code...

But one important thing seems to be missing at the first sight:
How to include a whole JS library into the GWT project and access its methods...

Magnus

Jens

unread,
Feb 25, 2014, 1:54:53 PM2/25/14
to google-we...@googlegroups.com
I have already said how to load it into the browser, so not sure what you mean. If you mean code completion in your IDE I have no idea how well Eclipse handles it. In IntelliJ it works pretty well.

-- J.

Magnus

unread,
Feb 25, 2014, 9:51:45 PM2/25/14
to google-we...@googlegroups.com
Ok, this must be the method with ClientBundle. I missed this.

It seems that you can also simply include it in the module xml:

Magnus

Magnus

unread,
Feb 25, 2014, 10:58:35 PM2/25/14
to google-we...@googlegroups.com
Hi,

including a script tag in the module xml did not work for me:

The Cross-Site-Iframe linker does not support <script> tags in the gwt.xml files

Using ClientBundle works.

However, what is still missing is the connection between the JS objects and the GWT widgets.
Consider the initial example:
var draw = SVG('drawing').size(300, 300)
var rect = draw.rect(100, 100).attr({ fill: '#f06' })
I can execute this code. But how can I place the drawing it creates into a GWT widget, e. g. a FlowPanel?

FlowPanel p = new FlowPanel ();
myClientBundle.executeJSCode();
p.add(???); // how to connect it???
 

Thanks
Magnus

Alain Ekambi

unread,
Feb 26, 2014, 2:52:32 AM2/26/14
to google-we...@googlegroups.com
One solution could be to create a GWT widget based on a DIV element and call the JSNI in the onLoad method.
I did something similar with a Google Map widget.

Have a look at 






--

Magnus

unread,
Feb 26, 2014, 4:32:50 AM2/26/14
to google-we...@googlegroups.com

On Wednesday, February 26, 2014 8:52:32 AM UTC+1, Alain wrote:
One solution could be to create a GWT widget based on a DIV element and call the JSNI in the onLoad method.
I did something similar with a Google Map widget.
 
 
Hello Alain,
 
thank you!
 
I understand that there is some DOM environment (e. g. a div) and that the JS code produces some other DOM content. And the goal is to inject the produced DOM content into the right DOM environment.
But I still cannot adapt this to my minimal example:
 
FlowPanel p = new FlowPanel ();
myClientBundle.executeJSCode();
 
Where is the result of executeJSCode and how can I move it right into my panel p?
 
Thanks
Magnus 

Alain Ekambi

unread,
Feb 26, 2014, 4:40:01 AM2/26/14
to google-we...@googlegroups.com
You basically create a GWT widget based on a div element. Once that widget is create you call your js code. Have a look at the onAttach method in the link I showed early on.

So you call call myClientBundle.executeJSCode() inside the onAttach method of your widget.

Hope that helps 





--

Magnus

unread,
Feb 26, 2014, 8:56:45 AM2/26/14
to google-we...@googlegroups.com
Hi Alain,
 
I understand that you fetch all the JS code with the method getJsObj() and pass it to the method setupMap. So I believe that setupMap will embed the JS code into the div element.
 
But what exactly is going on there?
(I do not know the Maps API):
 
private native void setUpMap(Element div, JavaScriptObject mapOptions)/*-{
var map = new $wnd.google.maps.Map(div, mapOptions);
this.@com.emitrom.pilot.maps.client.GMapWidget::map = @com.emitrom.pilot.maps.client.GMap::new(Lcom/google/gwt/core/client/JavaScriptObject;)(map);
}-*/;
 It looks like an object is created and put somewhere...
 
What would you do if the div were my FlowPanel p (see above) and the map were the draw object (see above)?
Where is the equivalence of p.add(draw)?
 
Thanks
Magnus
 
 

Andrei

unread,
Feb 26, 2014, 8:59:44 AM2/26/14
to google-we...@googlegroups.com
If this script is central to your application, you can simply include it in your host page. Then you can access it from your GWT code.

If this script is used only under certain conditions, you can inject it when necessary using a ScriptInjector.

Both approaches work well.

Magnus

unread,
Feb 26, 2014, 12:56:02 PM2/26/14
to google-we...@googlegroups.com
Hi,

I have a minimal example now, see below.
I included the JS code as a ClientBundle and also called injectScript:

public class SampleLoader
{
 public void injectScript()
 {
         String raw = SampleAssetsBundle.instance.myScript().getText();
         ScriptElement e = Document.get().createScriptElement();
         e.setText(raw);
         Document.get().getBody().appendChild(e);
 }
}

To test it, I use this method:

private static native Element testSVG ()
 /*-{
 var draw = SVG('drawing')
 
 var text = draw.text('SVG.JS').move(300, 0)
        text.font({
          family: 'Source Sans Pro'
        , size: 180
        , anchor: 'middle'
        , leading: 1
        })

 return (draw);  
 }-*/; 

But when call testSVG I get the following error:
com.google.gwt.core.client.JavaScriptException: (ReferenceError) @wgp.client.lib.GraphicsPanel::test()([]): SVG is not defined

When looking at the svg library (http://svgjs.com/), the method "SVG" should be included, but I cannot find it within the JS file.

So I actually don't know if it is not found for some reason, or doesn't exist or whatever.

What can I do next?

Thanks
Magnus




Jens

unread,
Feb 26, 2014, 3:03:33 PM2/26/14
to google-we...@googlegroups.com
But when call testSVG I get the following error:
com.google.gwt.core.client.JavaScriptException: (ReferenceError) @wgp.client.lib.GraphicsPanel::test()([]): SVG is not defined

You have added a <script> element to the browser's top window, but your GWT app runs inside an iframe. Inside that GWT iframe you can not access "SVG" directly, you have to write "$wnd.SVG". "$wnd" is created by GWT and always points to the browsers top window.

I think it is better to use GWT's ScriptInjector class instead of creating a <script> element yourself. ScriptInjector injects JavaScript code into the same iframe in which your GWT app runs by default. But you can tell ScriptInjector to inject to the top window if needed.

-- J.
Message has been deleted
Message has been deleted

Magnus

unread,
Feb 27, 2014, 12:25:07 AM2/27/14
to google-we...@googlegroups.com
Hi,

ok, I used the GWT ScriptInjector, and now also the identifier "SVG" can be resolved.
This brought me one step further, but now I get another error message:

[ERROR] [wgp] - Failed to load module 'wgp' from user agent 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36' at localhost:34277
com.google.gwt.core.client.JavaScriptException: (TypeError) @wgp.client.lib.GraphicsPanel::test()([]): Cannot read property 'nodeName' of null


I cannot debug JS code, but by tracing ($wnd.alert) I can verify that it happens at the call to SVG("drawing").

The complete code is shown below. What next? :-)

Magnus


Application.java:

public class Application implements EntryPoint
{
 public void onModuleLoad()
 {
  SampleLoader l = new SampleLoader();
  l.injectScript();
  
  final DrawBox dbx = new DrawBox ();
  dbx.setText ("Dialog Box");
  dbx.show ();
  
 }
}

SampleLoader.java:


public class SampleLoader
{
 public void injectScript()
 {
  String raw = SampleAssetsBundle.instance.myScript().getText();
  
  /*

  ScriptElement e = Document.get().createScriptElement();
  e.setText(raw);
  Document.get().getBody().appendChild(e);
  */
  
  ScriptInjector.fromString(raw).inject();
 }
}

SampleAssetBundle.java:

public interface SampleAssetsBundle extends ClientBundleWithLookup {

 public static final SampleAssetsBundle instance =
GWT.create(SampleAssetsBundle .class);

 @Source("svg.min.js")
 public TextResource myScript();
}


DrawBox.java:

public class DrawBox extends DialogBox
{
 //private FlowPanel pnl = new FlowPanel ();
 private GraphicsPanel pnl = new GraphicsPanel ();
 
 public DrawBox ()
 {
  super ();
  setText ("Dialog Box");
  
  pnl.setSize ("640px","480px");
  //add (pnl);
  setWidget (pnl);
  
  
  show ();
  center ();
  
 }
}

GraphicsPanel.java

public class GraphicsPanel extends ComplexPanel
{
 public GraphicsPanel()
 {
  Element e = test();
  setElement(e);
 }

 private static native Element test ()

Ümit Seren

unread,
Feb 27, 2014, 8:21:47 AM2/27/14
to google-we...@googlegroups.com
In your GraphicsPanel.java 

try to use 

var draw = $wnd.SVG('drawing') instead of SVG

Alberto Mancini

unread,
Feb 27, 2014, 8:59:22 AM2/27/14
to google-we...@googlegroups.com
Hi,
if I understand the js fragment you posted at the beginning of this thread

var
draw = SVG('drawing').size(300, 300)


means create an object SVG using as a parent element the element with the id 'drawing'
(cause 'drawing' is a string, you can pass an element instead, i checked the code)  so the error seems to indicate
that in your html there is not an element with id 'drawing'.

Hope it helps.
   Alberto.





On Thu, Feb 27, 2014 at 6:22 AM, Magnus <alpine...@gmail.com> wrote:
Hi,

ok, I used the GWT ScriptInjector, and now also the identifier "SVG" can be resolved.
This brought me one step further, but now I get another error message:

[ERROR] [wgp] - Failed to load module 'wgp' from user agent 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36' at localhost:34277
com.google.gwt.core.client.JavaScriptException: (TypeError) @wgp.client.lib.GraphicsPanel::test1()([]): Cannot read property 'nodeName' of null


I cannot debug JS code, but by tracing ($wnd.alert) I can verify that it happens at the call to SVG("drawing").

The complete code is shown below. What next? :-)

Magnus


Application.java:

public class Application implements EntryPoint
{
 public void onModuleLoad()
 {
  SampleLoader l = new SampleLoader();
  l.injectScript();
  
  final DrawBox dbx = new DrawBox ();
  dbx.setText ("Dialog Box");
  dbx.show ();
  
 }
}

SampleLoader.java:
public class SampleLoader
{
 public void injectScript()
 {
  String raw = SampleAssetsBundle.instance.myScript().getText();
  
  /*
  ScriptElement e = Document.get().createScriptElement();
  e.setText(raw);
  Document.get().getBody().appendChild(e);
 private static native Element test ()
 /*-{
 var draw = SVG('drawing')
 
 var text = draw.text('SVG.JS').move(300, 0)

 text.font({
          family: 'Source Sans Pro'
        , size: 180
        , anchor: 'middle'
        , leading: 1
        })

 return (draw);  
 }-*/; 

}

Magnus

unread,
Feb 27, 2014, 12:11:11 PM2/27/14
to google-we...@googlegroups.com


On Thursday, February 27, 2014 2:21:47 PM UTC+1, Ümit Seren wrote:
In your GraphicsPanel.java 

try to use 

var draw = $wnd.SVG('drawing') instead of SVG


When I do this, the error message changes to:

Object [object global] has no method 'SVG'


Well, I only injected svg.min.js from the homepage (http://svgjs.com/). I don't know JS well, but I feel that there is no declaration of "SVG" in this file. But the example is taken from the top of the homepage...

Magnus
 

Jens

unread,
Feb 27, 2014, 2:07:50 PM2/27/14
to google-we...@googlegroups.com
Working example:

public class SvgEntryPoint implements EntryPoint {

  public class SvgWidget extends Widget {

    public SvgWidget() {
      setElement(Document.get().createDivElement());
      // SVG library should draw inside the widget's container element
      drawExample(getElement());
    }

    private native void drawExample(Element container) /*-{
      var draw = SVG(container)

      // Must be used if JS is injected to top window, see below.
      // var draw = $wnd.SVG(container)

      draw.text('SVG.JS')
    }-*/;

  }

  public interface MyBundle extends ClientBundle {
    @Source("svg.min.js")
    TextResource svgJs();
  }

  public void onModuleLoad() {
    MyBundle bundle = GWT.create(MyBundle.class);
    // Injects JS code into the GWT iframe. SvgWidget.drawExample() can use SVG() then.
    ScriptInjector.fromString(bundle.svgJs().getText()).inject();


    // Injects JS code into the top level window. Now SvgWidget.drawExample() MUST use $wnd.SVG()
    // to reference the top level window. Only calling SVG() would mean to call it on the GWT iframe
    // to which the JS code has never been injected to.

    //ScriptInjector.fromString(bundle.svgJs().getText()).setWindow(ScriptInjector.TOP_WINDOW).inject();

    RootPanel.get().add(new SvgWidget());
  }

}


-- J.

Magnus

unread,
Feb 28, 2014, 10:42:21 PM2/28/14
to google-we...@googlegroups.com


On Thursday, February 27, 2014 8:07:50 PM UTC+1, Jens wrote:
Working example:

This works! Thanks!!!

Magnus 

Magnus

unread,
Mar 1, 2014, 6:33:32 AM3/1/14
to google-we...@googlegroups.com
Did anybody compare the two libraries, svg.js and snap.svg.js?
Why would you prefer one over the other?

Magnus
Reply all
Reply to author
Forward
0 new messages