Friday 7 December 2007

Simulate ajax file upload with wicket

First of all, we need to know upload is not supported by XmlHttpRequest so if we want to do ajax upload we need to use an Iframe. This iframe will upload the file and then notify the parent window with javascript. Here is a way to do that with wicket :


UploadPanel.java
package net.demay.fr.component.ajax.upload;

import org.apache.wicket.Page;
import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.WebComponent;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.form.upload.FileUpload;
import org.apache.wicket.markup.html.link.IPageLink;
import org.apache.wicket.markup.html.link.InlineFrame;
import org.apache.wicket.markup.html.panel.Panel;

/**
 * A panel allowing to upload file in an iframe. This panel can be used to
 * upload file in a "Ajax way" : page does not need to be reloaded. Only the iframe is reloaded
 * 
 * @author Vincent Demay
 *
 */
@SuppressWarnings("serial")
public abstract class UploadPanel extends Panel {

    private InlineFrame uploadIFrame = null;
    
    public UploadPanel(String id) {
        super(id);
        addOnUploadedCallback();
        setOutputMarkupId(true);
    }
   
    /**
     * Called when the upload load is uploaded and ready to be used
     * Return the url of the new uploaded resource
     * @param upload {@link FileUpload}
     */
    public abstract String onFileUploaded(FileUpload upload);
    
    /**
     * Called once the upload is finished and the traitment of the 
     * {@link FileUpload} has been done in {@link UploadPanel#onFileUploaded}
     * @param target an {@link AjaxRequestTarget}
     * @param fileName name of the file on the client side
     * @param newFileUrl Url of the uploaded file 
     */
    public abstract void onUploadFinished(AjaxRequestTarget target, String filename, String newFileUrl);
    
    protected void onBeforeRender() {
        super.onBeforeRender();
        if (uploadIFrame == null) {
            // the iframe should be attached to a page to be able to get its pagemap,
            // that's why i'm adding it in onBeforRender
            addUploadIFrame();
        }
    }

    /**
     * Create the iframe containing the upload widget
     *
     */
    private void addUploadIFrame() {
        IPageLink iFrameLink = new IPageLink() {
            public Page getPage() {
                return new UploadIFrame() {
                	@Override
                    protected String getOnUploadedCallback() {
                        return "onUpload_" + UploadPanel.this.getMarkupId();
                    }
                	
                    @Override
                    protected String manageInputSream(FileUpload upload) {
                    	return UploadPanel.this.onFileUploaded(upload);       
                    }
                };
            }
            public Class getPageIdentity() {
                return UploadIFrame.class;
            }            
        };
        uploadIFrame = new InlineFrame("upload", getPage().getPageMap(), iFrameLink);
        add(uploadIFrame);
    }

    /**
     * Hackie method allowing to add a javascript in the page defining the 
     * callback called by the innerIframe 
     *
     */
    private void addOnUploadedCallback() {
        final OnUploadedBehavior onUploadBehavior = new OnUploadedBehavior();
        add(onUploadBehavior);
        add(new WebComponent("onUploaded") {
            protected void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag) {
                // calling it through setTimeout we ensure that the callback is called
                // in the proper execution context, that is the parent frame
                replaceComponentTagBody(markupStream, openTag,
                        "function onUpload_" + UploadPanel.this.getMarkupId() +
                        "(clientFileName, newFileUrl) {window.setTimeout(function() { " +
                        onUploadBehavior.getCallback() + " }, 0 )}");
            } 
        });
    }
    
    private class OnUploadedBehavior extends AbstractDefaultAjaxBehavior {
        
        public String getCallback() {
            return generateCallbackScript(
                    "wicketAjaxGet('" + getCallbackUrl(false) +
                    "&newFileUrl=' + encodeURIComponent(newFileUrl)" +
                    " + '&clientFileName=' + encodeURIComponent(clientFileName)").toString();
        }
        
        protected void respond(AjaxRequestTarget target) {
        	UploadPanel.this.onUploadFinished(target, getRequest().getParameter("clientFileName"), getRequest().getParameter("newFileUrl"));
        }
    };

}

UploadPanel.html
<html xmlns:wicket>

<wicket:panel>

  <!-- I put the callback snippet at the body so that is rendered for each panel instead of once -->
  <script wicket:id="onUploaded" type="text/javascript"></script>

  <iframe wicket:id="upload" frameborder="0" height="55" width="450" style="overflow:hidden"></iframe>

</wicket:panel>

</html>
UploadIFrame.java
package net.demay.fr.component.ajax.upload;

import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.WebComponent;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.upload.FileUpload;
import org.apache.wicket.markup.html.form.upload.FileUploadField;
import org.apache.wicket.model.ResourceModel;

/**
 * A webPage to be used in an iframe in order to simulate
 * an ajax file upload
 * @author doume
 *
 */
@SuppressWarnings("serial")
public abstract class UploadIFrame extends WebPage {
    
    private boolean uploaded = false;
    private FileUploadField uploadField;
    private String newFileUrl;
    
    public UploadIFrame() {
        add(new UploadForm("form"));        
        addOnUploadedCallback();
    }
    
    /**
     * return the callback url when upload is finished
     * @return callback url when upload is finished
     */
    protected abstract String getOnUploadedCallback();
    
    /**
     * Called when the input stream has been uploaded and when it is available 
     * on server side
     * return the url of the uploaded file
     * @param upload fileUpload
     */
    protected abstract String manageInputSream(FileUpload upload);

    private class UploadForm extends Form {
    	public UploadForm(String id) {
            super(id);
            uploadField = new FileUploadField("file");
            add(uploadField);
            add(new AjaxLink("submit"){
            	@Override
            	protected void onClick(AjaxRequestTarget target) {
            		target.appendJavascript("showProgressWheel()");
            	}
            });
        }
        
        public void onSubmit() {
            FileUpload upload = uploadField.getFileUpload();  
            newFileUrl = manageInputSream(upload);
            //file is now uploaded, and the IFrame will be reloaded, during
            //reload we need to run the callback
            uploaded = true;
        }
        
    }
    
    private void addOnUploadedCallback() {
    	//a hacked component to run the callback on the parent
        add(new WebComponent("onUploaded") {
            protected void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag) {
                if (uploaded) {
                	if (uploadField.getFileUpload() != null){
	                    replaceComponentTagBody(markupStream, openTag,
	                            "window.parent." + getOnUploadedCallback() + "('" +
	                            uploadField.getFileUpload().getClientFileName() + "','" +
	                            newFileUrl +"')");
                	}
                    uploaded = false;
                }
            }            
        });
    }
}
UploadIframe.html
<html xmlns:wicket>

<head>

<script type="text/javascript">

function showProgressWheel() {
  document.images[0].style.display = 'block';
  // delay the wheel a bit so it can locally shine
  setTimeout(function() { document.forms[0].submit() }, 800);
  return false;
}

</script>

</head>

<body>

  <form wicket:id="form">
  	<table>
  		<tr>
  			<td>
    			<input wicket:id="file" type="file"/>
    		</td>
    		<td>
				<table wicket:id="submit"></table>
			</td>
			<td>
    			<img src="indicator.gif" style="display:none"/>
    		</td>
    	</tr>
    </table>
  </form>

  <script wicket:id="onUploaded" type="text/javascript">

</body>

</html>
Usage
final UploadPanel upload = new UploadPanel("myUpload"){
			
			@Override
			public String onFileUploaded(FileUpload upload) {
				if (upload != null){
			        try {
					//save on server side here
                                        //and return the url of the saved file
                                       return savedFile.getUrl()
			        } catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				return "";
			}

			@Override
			public void onUploadFinished(AjaxRequestTarget target, String filename, String newFileUrl) {
				//when upload is finished, will be called
			}
			
		};

Monday 22 October 2007

Make table tboby scroll with thead an tfoot fixed on both IE5+ anbd Firefox

Make a table to be fixed size with a given height and a thead and tfoot fixed is a real nightmare with Internet Explorer.
After spending a lot of time to do the follwing CSS to make it possible I decide to make this code public on my blog.
Live example

This code is Firefox and IE5+ Compatible

<html>
<head>
<style type="text/css">
div.tableContainer {
	width: 500px;		/* table width will be 99% of this*/
	height: 290px; 		/* must be greater than tbody*/
	overflow: auto;
	margin: 0 auto;
	}

table {
	width: 99%;		/*100% of container produces horiz. scroll in Mozilla*/
	border: none;
	background-color: #f7f7f7;
}

/*
 * Specific Firefox. Only Modern browser are able to interpret > . IE is not a modern browser
 */	
table>tbody { 
	overflow: auto; 
	height: 250px;
	overflow-x: hidden;
}

/*
 * Traget is IE5+ only. Only IE is able to interpret this kind of horrible expression Script
 * ----------
 * FOR HEADER
 */
thead tr {
	position:relative; 
	top: expression(offsetParent.scrollTop);
}

/*
 * Traget is IE5+ only. Only IE is able to interpret this kind of horrible expression Script
 * ----------
 * FOR FOOTER
 * ----------
 * Some explaination : 
 * with pseudo code : 
 * if (scroll_is_needed){
 * 	top = container_height + table_scrollTop - table_Height
 * } else {
 * 	//tfoot should be at the same place if there is a scroll or not
 *	top = container_height - table_height;
 * }
 */
table tfoot tr {
      position: relative; 
      overflow-x: hidden;
      top: expression(parentNode.parentNode.offsetHeight >= offsetParent.offsetHeight ? 
		offsetParent.offsetHeight + offsetParent.scrollTop - parentNode.parentNode.offsetHeight : 
		offsetParent.offsetHeight - parentNode.parentNode.offsetHeight);
}

/*
 * Classical Css
 */	
thead td, thead th, tfoot td {
	text-align: center;
	background-color: #C3C3C3;
	font-size : 12px;
	color: white;
	font-weight: bold;
	border-top: solid 1px gray;
}	
	
td {
	color: #666666;
	padding-right: 2px;
	text-align: center;
	font-size : 12px;
	border-bottom: solid 1px #C3C3C3; /* Do not mark double boder */
	border-left: solid 1px #C3C3C3;
}


/*
 * FF scroll hide last column.
 * prevent this case
 */
td:last-child {
	padding-right: 20px;
}
</style>


</head>

<body>
<div class="tableContainer">
  <table cellspacing="0">
     <thead>
      <tr> 
        <td width="30%">Header cell1</td>
        <td width="30%">Header cell2</td>

        <td width="20%">Header cell3</td>
        <td width="20%">Header cell 4</td>
      </tr>
    </thead>
    <tbody>
      <tr> 
        <td>a</td>
        <td>a</td>
        <td>a</td>
        <td>a</td>
      </tr>

     ...

      <tr> 
        <td>z</td>
        <td>z</td>
        <td>z</td>
        <td>z</td>
      </tr>

    </tbody>
	<tfoot>
	      <tr> 
	        <td colspan="5">Table footer repeats on print</td>
	      </tr>

    	</tfoot>
  </table>

</div>


</div> 
</body>
</html>


Monday 3 September 2007

A full js tab for wicket

Here is a code snippet to do a full js tab for wicket (tabs on the left) : TabPanel
[java]
package com.theveniceproject.cow.editors.ui.widgets;

import java.util.List;

import org.apache.wicket.ResourceReference;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.behavior.HeaderContributor;
import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
import org.apache.wicket.extensions.markup.html.tabs.ITab;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.list.Loop;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;

public class TabPanel extends Panel {

    private List tabs;
    private IModel tabCount;
    
    private static final ResourceReference DEFAULT_CSS = new ResourceReference(LeftTabPanel.class, "css/TabPanel.css");

    public TabPanel(String id, List<AbstractTab> tabs) {
        super(id, new Model());
        setOutputMarkupId(true);
        
        checkTabs(tabs);
        
        add(HeaderContributor.forCss(getCss()));
        add(HeaderContributor.forJavaScript(LeftTabPanel.class, "TabPanel.js"));
        
        //create a model for the tabs size
        this.tabs = tabs;
        tabCount = new AbstractReadOnlyModel()
        {
            private static final long serialVersionUID = 1L;

            public Object getObject()
            {
                return new Integer(LeftTabPanel.this.tabs.size());
            }
        };
        createTabs();
        createContents();
    }
    
    /**
     * Return the css to use to layout tabs
     * @return the css to use to layout tabs
     */
    public ResourceReference getCss() {
        return DEFAULT_CSS;
    }

    @Override
    protected void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag) {
        // TODO Auto-generated method stub
        super.onComponentTagBody(markupStream, openTag);
        getResponse().write("<script type=\"text/javascript\">var " + getMarkupId() + "Tabs = new TabManager('" + getMarkupId() + "'); ");   
    }
    
    /**
     * Method to check if tabs is all rightd
     * @param tabs tabs list
     */
    private void checkTabs(List<AbstractTab> tabs){
        if (tabs == null)
        {
            throw new IllegalArgumentException("argument [tabs] cannot be null");
        }

        if (tabs.size() < 1)
        {
            throw new IllegalArgumentException(
                    "argument [tabs] must contain a list of at least one tab");
        }
    }
    
    /**
     * Create tabs on the left of the component
     * @param tabsContainer
     */
    protected void createTabs(){
        add(new Loop("tabs", tabCount)
        {
            private static final long serialVersionUID = 1L;

            protected void populateItem(LoopItem item)
            {
                final int index = item.getIteration();
                final ITab tab = ((ITab)LeftTabPanel.this.tabs.get(index));
                
                final boolean selected = index == 0;
                
                WebMarkupContainer tabContent = new WebMarkupContainer("tab"){
                    @Override
                    protected void onComponentTag(ComponentTag tag) {
                        super.onComponentTag(tag);
                        tag.put("class", "tabs");
                        //connect onClick event
                        tag.put("onClick", LeftTabPanel.this.getMarkupId() + "Tabs.selectTab(this)");
                    } 
                   
                    @Override
                    protected void onBeforeRender() {
                        super.onBeforeRender();
                        if (selected){
                            add(new AttributeAppender("class", new Model("selected"), " "));
                        }
                    }
                };
                tabContent.add(new Label("title", tab.getTitle()));
                item.add(tabContent);
            }

        });
    }
    
    
    /**
     * Create the tab content using {@link AbstractTab} Panel
     */
    private void createContents(){
        add(new Loop("contents", tabCount)
        {
            private static final long serialVersionUID = 1L;

            protected void populateItem(LoopItem item)
            {
                final int index = item.getIteration();
                final ITab tab = ((ITab)LeftTabPanel.this.tabs.get(index));
                
                final boolean selected = index == 0;
                
                item.add(tab.getPanel("innerPanel"));
                
                if (!selected){
                    item.add(new AttributeAppender("style", true, new Model("display:none"), ";"));
                }
            }
        });
    }

}



TabPanel.html
<wicket:panel>
	<table width="100%" class="leftTab" cellpadding="0" cellspacing="0">
  		<tr valign="top">
			<td width="120px">
				<div wicket:id="tabs">
					<div wicket:id="tab">
						<span wicket:id="title"></span>
					</div>
				</div>
			</td>
			<td>
				<div class="content">
					<div  wicket:id="contents">
						<div wicket:id="innerPanel">
							content
						</div>
					</div>
				</div>
			</td>
		</tr>
	 </table>
</wicket:panel>


TabPanel.js
function TabManager(/**String*/ id){
	this.id = id;
	this.table = document.getElementById(id).getElementsByTagName("table")[0];
	
	/**
	 * Select the given tab and show its content
	 */
	this.selectTab = function(/** div */ tab){
		this._clearSelected();
		addClass(tab, "selected");
		this.setContentSelected(tab);
	}
	
	/**
	 * Return the list of domNode representing tabs
	 */
	this._getTabs /** Node[] */ = function(){
		return this.table.rows.item(0).cells.item(0).childNodes;
	}
	
	/**
	 * return the list of domNode Representing Content
	 */
	this._getContents /** Node[] */ = function(){
		return this.table.rows.item(0).cells.item(1).getElementsByTagName("div")[0].childNodes;
	}
	
	/**
	 * Clear Selection 
	 */
	this._clearSelected = function(){
		var tabs = this._getTabs();
		for (var i=0; i<tabs.length; i++){
			if (tabs[i].nodeName == "DIV"){
				removeClass(tabs[i].getElementsByTagName('div')[0], "selected");
			}
		}
	}
	
	/**
	 * Select the right Content making it visible block
	 */
	this.setContentSelected = function(/** div */ tab){
		var tabs = this._getTabs();
		var pos = 0;
		for (var i=0; i<tabs.length; i++){
			if (tabs[i].nodeName == "DIV"){
				if (tab == tabs[i].getElementsByTagName('div')[0]){
					break;
				}
				pos ++;
			}
		}
		var contents = this._getContents();
		var contentPos = 0;
		for (var i=0; i<contents.length; i++){
			if (contents[i].nodeName == "DIV"){
				if (contentPos == pos){
					contents[i].style.display="block";
				}else{
					contents[i].style.display="none";	
				}
				contentPos ++;
			}
		}
	}
}



function addClass(/**DomNode*/ node, /** String */ cssclass){
	if (!new RegExp('\\b'+cssclass+'\\b').test(node.className)){
		node.className+=node.className?' '+cssclass:cssclass;
	}
}

function removeClass(/**DomNode*/ node, /** String */ cssclass){
	var rep=node.className.match(' '+cssclass)?' '+cssclass:cssclass;
    node.className=node.className.replace(rep,'');
}


TabPanel.css
table.leftTab {
	border-spacing: 0px;
}

table.leftTab * div.tabs{
	padding:4px 15px 4px 6px;
	color:white;
	cursor:pointer;
	font-size:12px;
	font-weight:bold;
	white-space:nowrap;
	background-image: url('sidetabsDefault.gif');
	background-repeat: no-repeat;
}

table.leftTab * div.selected{
	color:blue;
	cursor: default;
	background-image: url('sidetabsSelected.gif');
}

table.leftTab * .content {
	border:2px solid blue;
	min-height: 300px;
	padding: 10px 25px 10px 25px;
	font-family:'Arial',sans-serif;
	font-size:12px;
}

table.leftTab * .content * table{
	font-family:'Arial',sans-serif;
	font-size:12px;
}

table.leftTab * .content * label{
	font-weight:bold;
	margin-right:50px;
}

Wednesday 8 August 2007

WicketStuff Dojo Beta is out

The first new WicketStuffDojo release is out. It is named 1.3.0-beta in order to follow wicket version number while it is still possible. WicketStuffDojo-1.3.0-beta depends on wicket-1.3.0-beta2. You can find it on wicketstuff public repo.

You can now use a release ;) . Let us know if you find some bugs (http://wicketstuff.org/jira/browse/DOJO)

Have good fun with it.

Thursday 2 August 2007

Keyboard events handling nightmare!!

Handling keyboard events is a real nightmare. If you want connect some keyboard events, this page can be usefull : http://unixpapa.com/js/key.html.
Moreover, you need to know Internet Explorer and firefox do no handle event on the same way:
firefox
myDomNode.onkeypress = function(event) { alert(event.keyCode)}

ie
myDomNode.onkeypress = function() { alert(window.event.keyCode)}
Actually IE propage key event on window.



So to deal with that mess you need to do
myDomNode.onkeypress = function(event) {if (!event){event = window.event;} alert(event.keyCode);}

Thursday 12 July 2007

Make model aware of its component with wicket1.3

Migrating from wicket 1.2.X to 1.3 is not really hard if code has been well written (of course ;) ). Something is quite annoying : model. Models are no longer aware of component using them. Here is a simple Abstract Class making model aware of component :
[Java]
public abstract class AbstractComponentAwareModel implements IWrapModel,
IComponentAssignedModel
{

   private Component component;
   
   public abstract Object getObject();
   public abstract void setObject(Object object);
   public void detach() {}
   
   /**
    * @see org.apache.wicket.model.IWrapModel#getWrappedModel()
    */
   public IModel getWrappedModel()
   {
       return this;
   }

   /**
    * @see org.apache.wicket.model.IComponentAssignedModel#wrapOnAssignment
(org.apache.wicket.Component)
    */
   public IWrapModel wrapOnAssignment(Component component)
   {
       this.component = component;
       return this;
   }

   protected Component getComponent()
   {
       return this.component;
   }
}
Thx to Johan Compagner for the tip

Monday 11 June 2007

A new WicketStuff commiter

Welcome on board to Grégory Maes who joins our wicketStuff-Dojo developer team.
Gregory made a lot of patches and can now close a lot of bugs applying his own modification ( = a little less work for each of us ;) ).

Thursday 7 June 2007

PickWick


Jbq, Titom and I are proud to announce the creation of a new WicketStuff Project: PickWick:
  • Provides a set of basic pages to render the pictures, organized in folders (called sequences)
  • Uses a nice URL displaying the folder name relative to the images directory
  • Back-office application to edit the photo gallery
  • Thumbnails and scaled images are generated on disk to avoid the complexity of caching systems
  • ImageMagick support, for maximum efficiency and to reduce the memory footprint of the JVM
  • Java2D support
PickWick has just been started, We will make more "buzz" when the future release will come out ;).

Saturday 10 February 2007

A new Wicket Commiter

Wicket team just elected a new commiter in the team. Yes it is my co-worker on Joost JBQ.

Congratulation JB ;)

Wicket 1.2.5 is out!

As I said in a previous post wicket, is now in apache incubator. But waiting for the first release of Apache Wicket, wicket team released its last Sourceforge Wicket which is Wicket 1.2.5.

You can download it on sourceforge.

Saturday 20 January 2007

Wicket 1.3 could coming out in January?

Eelco Hillenius says recently in the wicket dev mailing list wicket 1.3 could be released in January 2007. What a good news for Wicket-Contrib-Dojo (WCD) that could be released at same time as wicket.
Wicket release was delayed because it was moved onto Apache Software Fundation and some administrative update was required.

Waiting for this release, WCD is ready to be used on production and it is used in production application. As I mention in a previous post, I work for Joost, and we use wicket and WCD in some Joost application. This application is impressive, It looks like a desktop application as my co-worker (on Joost and WCD) mentions on his blog.

So you can use the current WCD from svn : https://wicket-stuff.svn.sourceforge.net/svnroot/wicket-stuff/wicket-1.3/wicket-contrib-dojo. Try It and enjoy.

Thursday 4 January 2007

Skipping test when compiling Wicket Trunk

Wicket compile itself with maven but the trunk version is full of test in error. So you can build it skipping tests with this kind of command.
mvn -U -cpu -Dmaven.test.skip=true install
Hope tests will soon fixed ;)

Thursday 30 November 2006

New Features in Wicket-dojo-contrib

For more than a week I've a lot of time to work on Wicket-Dojo-Contrib, so now there are a lot of cool new features such as Calendar or lot of new container. Take a look at it on my web live demo.
No distrib is avalaible for the moment because I work on trunk (future wicket 2.0) some features are backported on 1.3 branch wich will be compatible with future wicket 1.3. So be patient, I will try to make a new distribution as soon as wicket 1.3 will come out.

Tuesday 7 November 2006

Un point sur Wicket-Dojo-Contrib

Voila près de 15 jours que j'ai mis les mains dans le code de Wicket-Dojo-Contrib et voila les fonctionnalités que j'ai ajoutées/réparées :
  • DojoAjaxUpdate - permet de rafraichir des widgets sur modification d'un widget
  • DojoAutoUpdate - Placé sur un component, il permet de l'auto rafraichir à une période donnée
  • Drag'n'Drop - Evenement coté serveur lors du deplacement d'éléments
  • Effet de fondu, explode, wipe
  • DojoDialog
  • DojoFloatingPane
  • DojoTooltip - l'exemple ne fonctionne pas encore

Tout ces widgets ne seront disponibles que pour la prochaine version 2.0 de Wicket.

Des exemples de ces widgets sont disponibles ici

je pense que les prochaines integrations seront autour des TabContainer et des Tree
D'autres idées ?..

Sunday 22 October 2006

Integration de widget Dojo dansWicket

L'intégration de Dojo dans wicket est actuellement un peu pauvre, en effet aucun widget n'est encore integré. Pour l'instant seul les effets visuels et quelques mécanismes de base sont intégrés.

Je me suis donc motivé pour intégrer des nouveaux widgets et mécanismes. Pour l'instant je n'ai intégré que le Drag'n'Drop et le widget Dialog, cependant ceci ne fonctionne qu'avec la future version 2.0 de wicket et je n'ai pas encore fait le patch pour le bug reporter de Wicket-contrib-dojo sur SourceForge.
Ceci parceque j'ai changé la façon dont Wicket-contrib-dojo utilise Dojo et le patch fait plus de 4Mo donc je ne peut pas le soumettre sur le bug reporter. J'ai mis au courant les responsables du projet et j'attends une réponse pour savoir que faire de ce "gros patch" : http://www.jroller.com/page/ruudmarco?entry=woohoo_the_first_real_release#comments

Un petit message pour Vince : voilà le drag'n'drop existe maintenant dans Wicket ;)