JSNI window document $wnd $doc

GWT JSNI Variables – An Exhaustive List

 FROM HERE

 

$wnd
Reference to the root browser window GWT widgets are being rendered to. Note that when called from the iframe linker, GWT code runs in an iframe, not the root browser window, so calling window will reference the wrong window.
$doc
Same as above, but referencing the document, not window. Same as $wnd.document
$gwt_version
The X.Y.Z version of GWT this code was compiled with. Because Google uses the GWT trunk in their apps, they will return 0.0.999.
$moduleName
The name of the compiled GWT module. Note, you may have assumed this was obfuscated. It’s not.
$moduleBase
The root URL for this module’s entrypoint.
$strongName
The MD5 hash for this module.
$hosted
Url and port of code server in hosted mode. i.e. 127.0.0.1:9997
$hostedHtml Version
Version of hosted HTML page (hosted.html) in use.This is the page that shows error traces in browser in hosted mode.
$sessionId
Used when collecting stats in hosted mode. Same value as window.__gwt_SessionID
$stats()
Undocumented. Used to collect stats in hosted mode.
$entry()
Method that makes code reentry safe. This should wrap GWT code that will be called  from outside of GWT via Javascript calls you wish to expose from your GWT application. See docs for more.
$stack, $stackDepth, $location
Undocumented. Trace information used by JsStackEmulator.

 

Example of accessing GWT Version

To determine the running version of a GWT application, you can use JSNI to return that version.

1
2
3
4
// easily get the GWT version
public static native String getGwtVersion() /*-{
    return $gwt_version;
}-*/;

From outside the application you can walk the DOM of the page to find the iframe containing the running GWT code and access the version like below.

1
2
3
4
5
6
7
8
9
10
11
12
var gwtVersion = null;
var frames = document.getElementsByTagName('iframe');
for (var i=0; i<frames.length; i++) {
    // prevent security access errors
    try {
        if(frames[i].contentWindow.$gwt_version) {
            gwtVersion = frames[i].contentWindow.$gwt_version;
            break;
        }
    }
    catch(e) {}
}

This is the mechanism I used to detect GWT apps in the Library Detector Chrome Extension (Github). Note the wrapping in a try/catch. This is because some iframes are cross domain and will throw a security exception if you try to access them.

Some other explanations:

Window is a class plenty of static methods, which you can use for getting or setting certain properties of the browser window, but actually it is not the native browser window object. These method implementations are eventually JSNI blocks which use the $wnd variable to set/get those properties. Like Window.alert() whose implementation would be $wnd.alert(). Note that Window.java doesn’t have access for everything in the browser’s window object like window.console etc.

GWT uses $wnd instead of window because compiled code is executed normally in an iframe, and in this context, window will reference the iframe window while $wnd will reference the parent window. The same occurs with $doc which is a reference in the iframe to the parent document.

In the other side Document is a java class which extends JavaScriptObject, it means that it is an Overlaytype which basically means that it is a special wrapper for a native javascript object, it does not modify the underlying JavaScript but adds a set of java methods to interact with it. It can be safely cast when passing to jsni.

In summary, although Document and $doc are not the same in java world, when it is compiled they will be the same, otherwise Window it’s not an overlay of $wnd, it is just a way to access certain methods of the browser window.

Although GWT compiled code delegates to native js objects and methods, don’t try to find similarities between js and java objects. GWT has designed an API to develop ajax applications using a set of java objects, widgets, patterns, etc. Some objects and methods are named in the same way, but almost the API is different. There are though, other projects which compiles java to javascript which has a rigid parallelism between both worlds like ST-JS, and GWT provides an experimental library called Elemental whose API is almost identical to javascript (it’s only available for Chrome).

speed up gwt compilation

1. use ”

<draftCompile>true</draftCompile>

in the gwt-maven-plugin configuration.

2. use another build profile to limit permutation:

HERE is the detail .

To set gwt up, use :

mvn archetype:generate -DarchetypeGroupId=org.codehaus.mojo -DarchetypeArtifactId=gwt-maven-plugin -DarchetypeVersion=2.5.1 -DgroupId=com.goodtrendltd.gwt -DartifactId=MyDemo -Dversion=1.0-SNAPSHOT

gwt table td tr getOffsetHeight() inconsistency if border is set

In Gxt3 grid’s column header, if you set any border to the header row, your column header will grow. Also if you have filter in the header, even just hovering on the filter will cause the growth.

After digging into the code, the following code in ColumnHeader causes this: (my current version is 3.0.6)

This is the reason for the growth of hovering on filter .

    protected void activate() {
      if (!cm.isMenuDisabled(indexOf(this))) {
        XElement td = getElement().findParent(&quot;td&quot;, 3);
        int h = td.getHeight(true);
        if (!GXT.isChrome()) {
          getElement().setHeight(h, true);
        }
        if (btn != null) {
          XElement.as(btn).setHeight(h, true);
        }
        td.addClassName(styles.headOver());
      }
    }

This is the code in the protected void adjustColumnWidths(int[] columnWidths). which cause the growth when resize.

        if (rendered) {
          inner.setHeight(cell.getOffsetHeight(), true);
        }

The root cause is from the “getOffsetHeight()” method, for some reason if a border-top/bottom is set to tr,  when getting the OffsetHeight of the td, the result will be (real height + half of the tr border). Notice here i am talking about getting the offset height of td, not tr. However it still includes the border set in tr.  Theoretically, offsetHeight should be the height of the visible area for an object, in pixels. The value contains the height with the padding, scrollBar, and the border, but does not include the margin. Not sure whether this is a bug or a normal behavior.

A example here.

event loop gwt scheduleDeferred

A browser event loop is a thread started by the browser that is constantly scanning for and running different events, just like it sounds. As events occur they are put in the event queue and run in turn by the one event thread. Your javascript should not create its own loops waiting for it to complete or anything like that…it will block that one continuous event loop thread. Instead you would use something like setTimeout or setInterval and check for whatever conditions you are waiting for so the browser can do work while it ‘waits’.

GWT is nice in that it can co-opt this process somewhat using the scheduler — in your case where you want to run something after the event loop ‘completes’ you will probably want to use scheduleFinally or scheduleDeferred. It will inject a handler for a piece of code into the event queue so that it will run after all other code in the current execution context (current execution context == where ever you are in the current JavaScript object hierarchy with the window as the root object) is run but before the next event that is placed in the queue.

.


Example of setting focus

The TextBox needs to be attached to the page before you can focus it.
So, the following should fail:
TextBox textBox = new TextBox();
textBox.setFocus(true); // Fail: not attached to the DOM
RootPanel.get().add(textBox);
And the following should work:
TextBox textBox = new TextBox();
RootPanel.get().add(textBox);
textBox.setFocus(true); // Works: attached to the DOM

The reason a deferred command works is because the deferred command fires after the current event loop, which means that your text box gets attached.

TextBox textBox = new TextBox();
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
textBox.setFocus(true);
}
});
RootPanel.get().add(textBox); // Fires before setFocus
 

Notice that I said it should work.  In reality, focus can behave very weirdly, especially in IE.  I’ve found that if you create an element and try to focus it in the same event loop, the focus often fails in IE.  If your TextBox was already created (say in a constructor), it should be focusable as soon as you attach it to the DOM.  In either case, using a deferred command should always work.

We should be able to just make all calls to focus use a deferred command in IE, but then again maybe not.  What if you call focus() then blur() synchronously?  That means blur also needs to be in a deferred command.  I think it still works, but we’ll have to test all the use cases to make sure.

.


Example of enforcing text max length

Need a scheduler because we should truncate after value is set to the widget, without the scheduleDeferred, we are truncating the text that has not appended the current user type char.

This textarea is defined inside a DynoTextArea class.

    private TextArea textArea = new TextArea()
    {
        @Override
        public void onBrowserEvent(Event event)
        {
            // Checking for paste event
            if (event.getTypeInt() == Event.ONPASTE)
            {
                if (DynoTextArea.this.truncateWhenExceedMax)
                {
                    //need a scheduler here because we should truncate after value is set to the widget
                    Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand()
                    {

                        @Override
                        public void execute()
                        {
                            String deferedText = DynoTextArea.this.textArea.getValue();
                            if (deferedText != null && deferedText.length() > getMaxLength())
                            {
                                DynoTextArea.this.textArea.setValue(deferedText.substring(0, getMaxLength()));
                            }
                        }
                    });
                }
                return;
            }

            // Checking for keyUp event.
            if (event.getTypeInt() == Event.ONKEYDOWN)
            {
                String text = DynoTextArea.this.textArea.getValue() == null ? "" : DynoTextArea.this.textArea.getValue();
                //when truncate is set, only allow several keys when max is reached
                if (DynoTextArea.this.truncateWhenExceedMax && text.length() >= getMaxLength() && isDisplayCharacterEvent(event))
                {
                    event.preventDefault();
                    return;
                }
            }
            super.onBrowserEvent(event);
        }

    };

    /**
     * determint whether the passed in event is a display char key down event.
     * @param event
     * @return
     */
    public static boolean isDisplayCharacterEvent(Event event)
    {
        return event.getKeyCode() != KeyCodes.KEY_LEFT && event.getKeyCode() != KeyCodes.KEY_TAB && event.getKeyCode() != KeyCodes.KEY_RIGHT &&
            event.getKeyCode() != KeyCodes.KEY_DELETE && event.getKeyCode() != KeyCodes.KEY_BACKSPACE && event.getKeyCode() != KeyCodes.KEY_SHIFT &&
            event.getKeyCode() != KeyCodes.KEY_CTRL && event.getKeyCode() != KeyCodes.KEY_HOME && event.getKeyCode() != KeyCodes.KEY_END;
    }

gwt Widget AcceptsOneWidget Composite IsWidget SimplePanel

Let’s first separate interfaces from classes.

Interfaces are great for mocking (thus allowing for testing your app without the need for the sluggishGWTTestCase):

  • IsWidget: when all you need is a handle on a widget, without depending on the Widget class. This is typically used with MVP as a way to represent the view.
  • AcceptsOneWidget: when you need a placeholder for a single widget (in the form of an IsWidget). This is typically used with Activities, to insert the view (IsWidget) into the given slot(AcceptsOneWidget).

The classes you list all extend Widget, so they rely on JSNI and (most of the time) need to run in aGWT environment (for unit tests, that means a GWTTestCase):

  • Widget: the base of all widgets. Implements IsWidget returning itself from asWidget().
  • Composite: a base class when you need to create a widget built from other widgets while hiding their implementation. While you could extend an existing widget, it’s generally better to hide it inside a Composite so you only expose the API you need/want to expose. Composite is about “composition rather than inheritance” and encapsulation. Examples of composites in standard widgets include TabPanel (built from a TabBar and DeckPanel), DateBox (built from a TextBoxand DatePicker in a PopupPanel), ValueListBox that wraps a ListBox or ValuePicker that wraps a CellList. In many cases, given that panels accept IsWidget children, you could simply implement IsWidget rather extend Composite, but it’s sometimes useful to have a true Widget.
  • SimplePanel a panel that implements AcceptsOneWidget, useful as a slot when using activities (but you could also easily implement AcceptsOneWidget to insert into any kind of panel)

That being said, Google recently open-sourced GWT-Mockito that plugs Mockito into GWT.create()and uses classloader magic to rewrite JSNI methods and remove final modifiers so you can directly use widgets in tests without the need for GWTTestCase or MVP.

So, all in all, it depends how you approach your code, how you architecture your app. If you use MVP, stick to depending on interfaces only (IsWidgetAcceptsOneWidget) in your presenter so you can easily mock your view in your tests.
Otherwise, or if you want a “simplified MVP” where the view is a UiBinder template, try GWT-Mockito for your tests and directly use widgets.
Of course, you can mix both approaches in the same application. And in any case, build your own widgets as Widgets for low-level things (rarely needed), and Composites or IsWidgets for everything else, rather than extending existing widgets.

scheduler and timer in gwt

Comparison

Use Scheduler when you need a browser to complete whatever it is currently doing before you tell it to do something else. For example:

myDialogBox.show();
Scheduler.get().scheduleDeferred(new ScheduledCommand() {

    @Override
    public void execute() {
        myTextBox.setFocus();
    }
});

In this example, focus will not be set until the browser completes rendering of the dialog, so you tell the program to wait until the browser is ready.

Use Timer if you want some action to happen after a specified period of time. For example:

 notificationPanel.show();
 Timer timer = new Timer() {
     @Override
     public void run() {
         notificationPanel.hide();
     }
 };
 timer.schedule(10000);

This code will show notificationPanel, and then it will hide it after 10 seconds.

 

Further explanation on scheduler:

JavaScript (in a browser) is single threaded. The event loop model means, we’re always in exactly one of two states:

  • in the event loop
  • executing an event handler

There are many kinds of events: Click events, onload events, XHR events, timer events, … You’ll have to declare some handlers (at least one during page load), otherwise none of your code will ever be executed. One of them is the handler you specify by implementing onModuleLoad.

It’s important to keep all handlers short, because there’s no parallelism and no interrupts (except for the last resort “unresponsive script” interrupt). This means, that users can’t interact with the interface, until the browser returns to the event loop – and that doesn’t happen before the current handler is finished.

So if you want to defer some code until after the other event handlers had a chance, then you can useScheduler.scheduleDeferred.

Scheduler.scheduleIncremental helps you to split really long running tasks into multiple steps, giving the other event handlers a chance between each of the steps.

Scheduler.scheduleFinally just means: After handling our current handler (even if an exception occurs), but before returning to the event loop, execute my command.