27 marzo 2011

GWT detect popup blocker (rilevazione del blocco popup)

Indice dei tutorials: http://gheryd.blogspot.com/2011/06/javascript-gwt-tutorials.html

english version

Ho avuto l'esigenza di verificare se il browser in cui girava la mia applicazione gwt, avesse attivo il blocco dei popup.
Spulciando un po' in giro ho trovato del codice javascript che ho utilizzato in una classe jsni  gwt .
Ho inserito il codice javascript dentro dei native methods. Il sistema e' un po' rozzo ma funzionante (per ora l'ho solo testato su Chrome, eventualmente si può migliorare dentro i native methods).
La classe ha solo metodi statici ed è sufficiente avviare la rilevazione (detect) nel seguente maniera:
PopupBlockerController.detect();
Dopo 4 secondi (periodMillis) viene verificato se il il popup è stato correttamente aperto.
E' possibile così sapere se c'è un blocco pupup:
if( PopupBlockerController.isPopupBlocked() ) {
    Window.alert("popup bloccati");
}
La cosa migliore sarebbe avere un handler che viene chiamato quando i 4 secondi sono passati, ma purtroppo per ora non ho avuto il tempo. Dimenticavo una cosa importante, occorre creare un file html "popuptest.html" con il seguente codice:
popuptest.html
<!DOCTYPE html PUBLIC PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"; >
<head>
<title>Popup test</title>
<script type="text/javascript" language="Javascript">
function test() { return window.innerHeight!=0; }
</script>
</head>
<body>DETECT POPUP BLOCKER</body>
</html>
Questo html viene mostrato nel popup che il PopupBlockerController cercherà di aprire e contiene la funzione javascript test() che verrà richiamata dopo 4 secondi.
public class PopupBlockerController {


    private final static int periodMillis = 4000;


    public static void detect() {
       startPopupBlockerDetection();
       timer.schedule(periodMillis);
    }

    private static Timer timer = new Timer() {
        @Override
        public void run() {
            if( !isPopupBlocked() ) {
               checkTestPopup();
            }
        }
    };

    public static boolean isPopupBlocked() {
       return isPopupBlockedNative();
    }


    private static native boolean isPopupBlockedNative() /*-{
        return $wnd.popUpsBlocked;
    }-*/;

 
    private static native void startPopupBlockerDetection() /*-{
        $wnd.popUpsBlocked = false;
        $wnd.testPopup = $wnd.open('popuptest.html', 'popuptest', 'width=1px,height=1px,left=0,top=0,scrollbars=no');
        if( !$wnd.testPopup || $wnd.testPopup.closed || 
                   typeof $wnd.testPopup.closed=='undefined' )  {
            $wnd.popUpsBlocked = true
           if($wnd.testPopup) $wnd.testPopup.close();
         } else {
            $wnd.popUpsBlocked = false;
         }
     }-*/;


    private static native void checkTestPopup() /*-{
       if($wnd.testPopup) {
          if( $wnd.testPopup.test( )) {
               $wnd.popUpsBlocked = false;
          } else {
             $wnd.popUpsBlocked = true;
          }
             $wnd.testPopup.close();
       }
    }-*/;
}

15 marzo 2011

gwt widget tree patch for hidden items

Indice dei tutorials: http://gheryd.blogspot.com/2011/06/javascript-gwt-tutorials.html

Mi è capitato di avere l'esigenza di nascondere degli item del widget tree di gwt, come nell'esempio seguente:
Tree tree = new Tree();
  TreeItem root = new TreeItem("root");
  tree.addItem(root);
  for(int i=0;i<10;i++) 
      root.addItem( new TreeItem( "item_"+i ) );
  root.getItem(5).setVisible(false);
L'item con indice 5 viene nascosto, ma se si usano i tasti freccia su e giu, è selezionabile come tutti gli altri. Chiaramente questo non ha senso. Quindi ritengo questo un bug!
Ho risolto questo problema estendendo la classe Tree (api tree). Con un override del metodo "onBrowserEvent", intercetto l'evento prima che venga gestito dalla classe Tree.
Se l'evento è relativo alla tastiera (keyboard event) , allora l'oggetto "event" viene salvato e poi inoltrato alla metodo analogo della classe madre che procederà quindi nella selezione.
Dato che questo evento scatena una chiamata di eventuali SelectionHandler registrati, quanto istanzio la classe TreeExt, occorre necessariamente aggiungere l'handler in cui verrà verificato se l'item selezionato è nascosto (hidden). In tal caso viene chiamato il metodo fireLastEvent che di fatto è come se l'utente ripremesse lo stesso tasto. Quindi l'ultimo evento da tastiera verrà scatenato ripetutamente via codice finchè non verrà selezionato un item visibile.
public class TreeExt extends Tree {  
     private Event lastEvent = null;   
     TreeExt() {}
  
     @Override  
     public void onBrowserEvent(Event event) {  
       int eventType = DOM.eventGetType(event);  
       lastEvent = 
              (eventType== Event.ONKEYPRESS || eventType == Event.ONKEYDOWN) ? 
                  event : null;  
       super.onBrowserEvent(event);  
     }  
     public void fireLastEvent(){  
       if( lastEvent !=null ) {  
         super.onBrowserEvent(lastEvent);  
       }  
     }  
 }  
// Example  
 final TreeExt t = new TreeExt();  
 t.addSelectionHandler( new SelectionHandler() {  
    @Override  
    public void onSelection(SelectionEvent event) {  
      TreeItem item = event.getSelectedItem();  
      if( !item.isVisible() ) {  
         //Window.alert("not visible");  
         t.fireLastEvent();  
         return;  
      }  
      //TODO handle event for visible item  
   }  
 });  
E' possile sovrascrivere il metodo addSelectionHandler per fare in modo che sia la stessa classe TreeExt a controllare se l'item è visibile- Il codice per chi utilizza questa classe sarebbe più pulito ed elegante. Beh, se questo tocco di eleganza vi gusta allora provateci ;)