[richfaces-svn-commits] JBoss Rich Faces SVN: r9121 - in trunk: sandbox/api/src/main and 53 other directories.

richfaces-svn-commits at lists.jboss.org richfaces-svn-commits at lists.jboss.org
Thu Jun 19 15:21:31 EDT 2008


Author: nbelaevski
Date: 2008-06-19 15:21:31 -0400 (Thu, 19 Jun 2008)
New Revision: 9121

Added:
   trunk/framework/impl/src/main/java/org/richfaces/component/util/AbstractMessageUtil.java
   trunk/sandbox/api/src/main/java/org/richfaces/event/ChangeColumnVisibilityEvent.java
   trunk/sandbox/api/src/main/java/org/richfaces/event/ChangeColumnVisibilityListener.java
   trunk/sandbox/api/src/main/java/org/richfaces/event/ColumnResizeEvent.java
   trunk/sandbox/api/src/main/java/org/richfaces/event/ColumnResizeListener.java
   trunk/sandbox/api/src/main/java/org/richfaces/event/DragDropEvent.java
   trunk/sandbox/api/src/main/java/org/richfaces/event/DragDropListener.java
   trunk/sandbox/api/src/main/java/org/richfaces/messages/
   trunk/sandbox/api/src/main/java/org/richfaces/messages/SandboxMessageUtil.java
   trunk/sandbox/api/src/main/java/org/richfaces/model/DataProvider.java
   trunk/sandbox/api/src/main/java/org/richfaces/model/ExtendedDataTableModifiableModel.java
   trunk/sandbox/api/src/main/java/org/richfaces/model/ExtendedTableDataModel.java
   trunk/sandbox/api/src/main/java/org/richfaces/model/ExtendedTableDataModelWrapper.java
   trunk/sandbox/api/src/main/resources/
   trunk/sandbox/api/src/main/resources/org/
   trunk/sandbox/api/src/main/resources/org/richfaces/
   trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/
   trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/
   trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages.properties
   trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages_de.properties
   trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages_en.properties
   trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages_pl.properties
   trunk/sandbox/impl/src/main/java/org/richfaces/component/
   trunk/sandbox/impl/src/main/java/org/richfaces/component/util/
   trunk/sandbox/impl/src/main/java/org/richfaces/component/util/SandboxMessageUtil.java
   trunk/sandbox/ui/extendedDataTable/
   trunk/sandbox/ui/extendedDataTable/generatescript.xml
   trunk/sandbox/ui/extendedDataTable/pom.xml
   trunk/sandbox/ui/extendedDataTable/src/
   trunk/sandbox/ui/extendedDataTable/src/main/
   trunk/sandbox/ui/extendedDataTable/src/main/config/
   trunk/sandbox/ui/extendedDataTable/src/main/config/component/
   trunk/sandbox/ui/extendedDataTable/src/main/config/component/ExtendedDataTable.xml
   trunk/sandbox/ui/extendedDataTable/src/main/config/component/README
   trunk/sandbox/ui/extendedDataTable/src/main/java/
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/ExtendedTableColumnsIterator.java
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/README
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/SortedColumnsIterator.java
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/UIExtendedDataTable.java
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/UIExtendedDataTableState.java
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/AbstractExtendedRowsRenderer.java
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/AbstractExtendedTableRenderer.java
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/ExtendedTableHolder.java
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/DraggableRendererContributor.java
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/DropzoneRendererContributor.java
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/RichTableMenuRenderer.java
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/SimpleTableMenuRenderer.java
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/TableDragDropRenderer.java
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/TableMenuRenderer.java
   trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/TableSelectionRendererContributor.java
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/box/
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/box/Box.js
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/utils/
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/utils/StringBuilder.js
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/utils/Utils.js
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/utils/Validators.js
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/controls/
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/controls/datatable/
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/controls/datatable/ExtendedDataTable.js
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/controls/datatable/ExtendedDataTableHeader.js
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/controls/datatable/ExtendedDataTableSelection.js
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUILib.js
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/common/
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/common/prototype/
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/common/prototype/ext/
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/common/prototype/ext/extend.js
   trunk/sandbox/ui/extendedDataTable/src/main/javascript/common/scriptaculous/
   trunk/sandbox/ui/extendedDataTable/src/main/resources/
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/css/
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/css/extendedDataTable.xcss
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/checked.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/col-move-bottom.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/col-move-top.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/columns.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/columns.png
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/grid3-hd-btn.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/group-by.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/group-by.png
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/hmenu-asc.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/hmenu-desc.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/item-over.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/loading.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/menu-sort-asc.png
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/menu-sort-desc.png
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/menu.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/menuHover.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/minusIcon.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/plusIcon.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/s.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/sep-span.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/unchecked.gif
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/scripts/
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/scripts/dt-drag-indicator.js
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/scripts/simple-draggable.js
   trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/scripts/simple-dropzone.js
   trunk/sandbox/ui/extendedDataTable/src/main/templates/
   trunk/sandbox/ui/extendedDataTable/src/main/templates/README
   trunk/sandbox/ui/extendedDataTable/src/main/templates/org/
   trunk/sandbox/ui/extendedDataTable/src/main/templates/org/richfaces/
   trunk/sandbox/ui/extendedDataTable/src/main/templates/org/richfaces/htmlExtendedDataTable.jspx
   trunk/sandbox/ui/extendedDataTable/src/test/
   trunk/sandbox/ui/extendedDataTable/src/test/java/
   trunk/sandbox/ui/extendedDataTable/src/test/java/org/
   trunk/sandbox/ui/extendedDataTable/src/test/java/org/richfaces/
   trunk/sandbox/ui/extendedDataTable/src/test/java/org/richfaces/component/
   trunk/sandbox/ui/extendedDataTable/src/test/java/org/richfaces/component/JSFComponentTest.java
Modified:
   trunk/framework/impl/src/main/java/org/richfaces/component/util/MessageUtil.java
   trunk/sandbox/impl/pom.xml
   trunk/sandbox/ui/pom.xml
   trunk/ui/dataTable/src/main/config/component/columnAttributes.ent
   trunk/ui/dataTable/src/main/java/org/richfaces/component/UIColumn.java
Log:
Initial import for extendedDataTable

Added: trunk/framework/impl/src/main/java/org/richfaces/component/util/AbstractMessageUtil.java
===================================================================
--- trunk/framework/impl/src/main/java/org/richfaces/component/util/AbstractMessageUtil.java	                        (rev 0)
+++ trunk/framework/impl/src/main/java/org/richfaces/component/util/AbstractMessageUtil.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,124 @@
+/**
+ * License Agreement.
+ *
+ *  JBoss RichFaces - Ajax4jsf Component Library
+ *
+ * Copyright (C) 2007  Exadel, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+package org.richfaces.component.util;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.faces.application.Application;
+import javax.faces.application.FacesMessage;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+
+/**
+ * Created 19.06.2008
+ * @author Nick Belaevski
+ * @since 3.2.2
+ */
+
+public class AbstractMessageUtil {
+	private static final ResourceBundle getResourceBundle(String baseName, Locale locale, ClassLoader loader) {
+		if (loader != null) {
+			return ResourceBundle.getBundle(baseName, locale, loader);
+		} else {
+			return ResourceBundle.getBundle(baseName, locale);
+		}
+	}
+	
+	private static final FacesMessage getMessage(FacesContext context, String messageId, 
+			Object[] parameters, Locale locale, String baseBundleName) {
+		String summary = null;
+		String detail = null;
+		
+		ClassLoader loader = Thread.currentThread().getContextClassLoader();
+		
+		if (context != null) {
+			Application application = context.getApplication();
+			if (application != null) {
+				String messageBundleName = application.getMessageBundle();
+	
+				if (messageBundleName != null) {
+					ResourceBundle bundle = getResourceBundle(messageBundleName, locale, loader);
+					if (bundle != null) {
+						try {
+							summary = bundle.getString(messageId);
+							detail = bundle.getString(messageId + "_detail");
+						} catch (MissingResourceException e) {
+							//do nothing
+						}
+					}
+				}
+			}
+		}
+
+		if (summary == null) {
+			ResourceBundle bundle = getResourceBundle(baseBundleName, locale, loader);
+			try {
+				summary = bundle.getString(messageId);
+				
+				if (summary == null) {
+					return null;
+				}
+
+				detail = bundle.getString(messageId + "_detail");
+			} catch (MissingResourceException e) {
+				//do nothing
+			}
+		}
+		
+		String formattedSummary = MessageFormat.format(summary, parameters);
+		String formattedDetail = null;
+		if (detail != null) {
+			formattedDetail = MessageFormat.format(detail, parameters);
+		}
+		
+		return new FacesMessage(formattedSummary, formattedDetail);
+	}
+	
+	protected static final FacesMessage getMessage(FacesContext context, String messageId, 
+			Object[] parameters, String baseBundleName) {
+		
+		Locale locale;
+		FacesMessage result = null;
+		
+		if (context != null) {
+			UIViewRoot viewRoot = context.getViewRoot();
+			if (viewRoot != null) {
+				locale = viewRoot.getLocale();
+			
+				if (locale != null) {
+					result = getMessage(context, messageId, parameters, locale, baseBundleName);
+				}
+			}
+		}
+		
+		if (result == null) {
+			locale = Locale.getDefault();
+			result = getMessage(context, messageId, parameters, locale, baseBundleName);
+		}
+
+		return result;
+	}
+	
+}

Modified: trunk/framework/impl/src/main/java/org/richfaces/component/util/MessageUtil.java
===================================================================
--- trunk/framework/impl/src/main/java/org/richfaces/component/util/MessageUtil.java	2008-06-19 17:13:04 UTC (rev 9120)
+++ trunk/framework/impl/src/main/java/org/richfaces/component/util/MessageUtil.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -21,15 +21,9 @@
 
 package org.richfaces.component.util;
 
-import java.text.MessageFormat;
-import java.util.Locale;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
-
 import javax.faces.application.Application;
 import javax.faces.application.FacesMessage;
 import javax.faces.component.UIComponent;
-import javax.faces.component.UIViewRoot;
 import javax.faces.context.FacesContext;
 import javax.faces.el.ValueBinding;
 
@@ -38,7 +32,7 @@
  * created 06.02.2007
  * 
  */
-public class MessageUtil {
+public class MessageUtil extends AbstractMessageUtil {
 	private static final boolean IS_12;
 	
 	static {
@@ -73,86 +67,9 @@
 		return o;
 	}
 	
-	private static final ResourceBundle getResourceBundle(String baseName, Locale locale, ClassLoader loader) {
-		if (loader != null) {
-			return ResourceBundle.getBundle(baseName, locale, loader);
-		} else {
-			return ResourceBundle.getBundle(baseName, locale);
-		}
-	}
-	
-	private static final FacesMessage getMessage(FacesContext context, String messageId, 
-			Object[] parameters, Locale locale) {
-		String summary = null;
-		String detail = null;
-		
-		ClassLoader loader = Thread.currentThread().getContextClassLoader();
-		
-		if (context != null) {
-			Application application = context.getApplication();
-			if (application != null) {
-				String messageBundleName = application.getMessageBundle();
-	
-				if (messageBundleName != null) {
-					ResourceBundle bundle = getResourceBundle(messageBundleName, locale, loader);
-					if (bundle != null) {
-						try {
-							summary = bundle.getString(messageId);
-							detail = bundle.getString(messageId + "_detail");
-						} catch (MissingResourceException e) {
-							//do nothing
-						}
-					}
-				}
-			}
-		}
-
-		if (summary == null) {
-			ResourceBundle bundle = getResourceBundle(FacesMessage.FACES_MESSAGES, locale, loader);
-			try {
-				summary = bundle.getString(messageId);
-				
-				if (summary == null) {
-					return null;
-				}
-
-				detail = bundle.getString(messageId + "_detail");
-			} catch (MissingResourceException e) {
-				//do nothing
-			}
-		}
-		
-		String formattedSummary = MessageFormat.format(summary, parameters);
-		String formattedDetail = null;
-		if (detail != null) {
-			formattedDetail = MessageFormat.format(detail, parameters);
-		}
-		
-		return new FacesMessage(formattedSummary, formattedDetail);
-	}
-	
 	public static final FacesMessage getMessage(FacesContext context, String messageId, 
 			Object[] parameters) {
 
-		Locale locale;
-		FacesMessage result = null;
-		
-		if (context != null) {
-			UIViewRoot viewRoot = context.getViewRoot();
-			if (viewRoot != null) {
-				locale = viewRoot.getLocale();
-			
-				if (locale != null) {
-					result = getMessage(context, messageId, parameters, locale);
-				}
-			}
-		}
-		
-		if (result == null) {
-			locale = Locale.getDefault();
-			result = getMessage(context, messageId, parameters, locale);
-		}
-
-		return result;
+		return AbstractMessageUtil.getMessage(context, messageId, parameters, FacesMessage.FACES_MESSAGES);
 	}
 }

Added: trunk/sandbox/api/src/main/java/org/richfaces/event/ChangeColumnVisibilityEvent.java
===================================================================
--- trunk/sandbox/api/src/main/java/org/richfaces/event/ChangeColumnVisibilityEvent.java	                        (rev 0)
+++ trunk/sandbox/api/src/main/java/org/richfaces/event/ChangeColumnVisibilityEvent.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,47 @@
+/**
+ * 
+ */
+package org.richfaces.event;
+
+import javax.faces.component.UIComponent;
+import javax.faces.event.FacesEvent;
+import javax.faces.event.FacesListener;
+
+/**
+ * @author pawelgo
+ *
+ */
+public class ChangeColumnVisibilityEvent extends FacesEvent {
+	
+	private static final long serialVersionUID = 178097339066092532L;
+	
+	private String columnId;
+	
+	public ChangeColumnVisibilityEvent(UIComponent component, String columnId) {
+		super(component);
+		this.columnId = columnId;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.faces.event.FacesEvent#isAppropriateListener(javax.faces.event.FacesListener)
+	 */
+	public boolean isAppropriateListener(FacesListener listener) {
+		return (listener instanceof ChangeColumnVisibilityListener);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.faces.event.FacesEvent#processListener(javax.faces.event.FacesListener)
+	 */
+	public void processListener(FacesListener listener) {
+		((ChangeColumnVisibilityListener)listener).processChangeVisibility(this);
+	}
+	
+	public String getColumnId() {
+		return columnId;
+	}
+
+	public void setColumnId(String columnId) {
+		this.columnId = columnId;
+	}
+
+}

Added: trunk/sandbox/api/src/main/java/org/richfaces/event/ChangeColumnVisibilityListener.java
===================================================================
--- trunk/sandbox/api/src/main/java/org/richfaces/event/ChangeColumnVisibilityListener.java	                        (rev 0)
+++ trunk/sandbox/api/src/main/java/org/richfaces/event/ChangeColumnVisibilityListener.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,15 @@
+/**
+ * 
+ */
+package org.richfaces.event;
+
+import javax.faces.event.FacesListener;
+
+/**
+ * @author pawelgo
+ *
+ */
+public interface ChangeColumnVisibilityListener extends FacesListener {
+	
+	public void processChangeVisibility(ChangeColumnVisibilityEvent event);
+}

Added: trunk/sandbox/api/src/main/java/org/richfaces/event/ColumnResizeEvent.java
===================================================================
--- trunk/sandbox/api/src/main/java/org/richfaces/event/ColumnResizeEvent.java	                        (rev 0)
+++ trunk/sandbox/api/src/main/java/org/richfaces/event/ColumnResizeEvent.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,47 @@
+/**
+ * 
+ */
+package org.richfaces.event;
+
+import javax.faces.component.UIComponent;
+import javax.faces.event.FacesEvent;
+import javax.faces.event.FacesListener;
+
+/**
+ * @author pawelgo
+ *
+ */
+public class ColumnResizeEvent extends FacesEvent {
+	
+	private static final long serialVersionUID = -8424890931071484456L;
+	
+	private String columnWidths;
+
+	public ColumnResizeEvent(UIComponent component, String columnWidths) {
+		super(component);
+		this.columnWidths = columnWidths;
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.faces.event.FacesEvent#isAppropriateListener(javax.faces.event.FacesListener)
+	 */
+	public boolean isAppropriateListener(FacesListener listener) {
+		return (listener instanceof ColumnResizeListener);
+	}
+
+	/* (non-Javadoc)
+	 * @see javax.faces.event.FacesEvent#processListener(javax.faces.event.FacesListener)
+	 */
+	public void processListener(FacesListener listener) {
+		((ColumnResizeListener)listener).processColumnResize(this);
+	}
+	
+	public String getColumnWidths() {
+		return columnWidths;
+	}
+
+	public void setColumnWidths(String columnWidths) {
+		this.columnWidths = columnWidths;
+	}
+
+}

Added: trunk/sandbox/api/src/main/java/org/richfaces/event/ColumnResizeListener.java
===================================================================
--- trunk/sandbox/api/src/main/java/org/richfaces/event/ColumnResizeListener.java	                        (rev 0)
+++ trunk/sandbox/api/src/main/java/org/richfaces/event/ColumnResizeListener.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,15 @@
+/**
+ * 
+ */
+package org.richfaces.event;
+
+import javax.faces.event.FacesListener;
+
+/**
+ * @author pawelgo
+ *
+ */
+public interface ColumnResizeListener extends FacesListener {
+	
+	public void processColumnResize(ColumnResizeEvent event);
+}

Added: trunk/sandbox/api/src/main/java/org/richfaces/event/DragDropEvent.java
===================================================================
--- trunk/sandbox/api/src/main/java/org/richfaces/event/DragDropEvent.java	                        (rev 0)
+++ trunk/sandbox/api/src/main/java/org/richfaces/event/DragDropEvent.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,67 @@
+/**
+ * 
+ */
+package org.richfaces.event;
+
+import javax.faces.component.UIComponent;
+import javax.faces.event.FacesEvent;
+import javax.faces.event.FacesListener;
+
+/**
+ * @author pawelgo
+ *
+ */
+public class DragDropEvent extends FacesEvent {
+	
+	private static final long serialVersionUID = 2589493255876683993L;
+	
+	private Object dragValue;
+	private Object dropValue;
+	private String dragType;
+	private boolean dropBefore;
+
+	public DragDropEvent(UIComponent component) {
+		super(component);
+	}
+
+	public void processListener(FacesListener listener) {
+		((DragDropListener)listener).processDragDrop(this);
+	}
+
+	public Object getDragValue() {
+		return dragValue;
+	}
+
+	public void setDragValue(Object dragValue) {
+		this.dragValue = dragValue;
+	}
+
+	public Object getDropValue() {
+		return dropValue;
+	}
+
+	public void setDropValue(Object dropValue) {
+		this.dropValue = dropValue;
+	}
+	
+	public String getDragType() {
+		return dragType;
+	}
+	
+	public void setDragType(String dragType) {
+		this.dragType = dragType;
+	}
+	
+	public boolean isDropBefore() {
+		return dropBefore;
+	}
+
+	public void setDropBefore(boolean dropBefore) {
+		this.dropBefore = dropBefore;
+	}
+
+	public boolean isAppropriateListener(FacesListener listener) {
+		return (listener instanceof DragDropListener);
+	}
+
+}

Added: trunk/sandbox/api/src/main/java/org/richfaces/event/DragDropListener.java
===================================================================
--- trunk/sandbox/api/src/main/java/org/richfaces/event/DragDropListener.java	                        (rev 0)
+++ trunk/sandbox/api/src/main/java/org/richfaces/event/DragDropListener.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,15 @@
+/**
+ * 
+ */
+package org.richfaces.event;
+
+import javax.faces.event.FacesListener;
+
+/**
+ * @author pawelgo
+ *
+ */
+public interface DragDropListener extends FacesListener {
+	
+	public void processDragDrop(DragDropEvent event);
+}

Added: trunk/sandbox/api/src/main/java/org/richfaces/messages/SandboxMessageUtil.java
===================================================================
--- trunk/sandbox/api/src/main/java/org/richfaces/messages/SandboxMessageUtil.java	                        (rev 0)
+++ trunk/sandbox/api/src/main/java/org/richfaces/messages/SandboxMessageUtil.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,119 @@
+/**
+ * 
+ */
+package org.richfaces.messages;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.faces.application.Application;
+import javax.faces.application.FacesMessage;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+
+/**
+ * @author pawelgo
+ *
+ */
+public class SandboxMessageUtil {
+	
+	public static final String MESSAGES = "org.richfaces.sandbox.Messages";
+	
+	private static final ResourceBundle getResourceBundle(String baseName, Locale locale, ClassLoader loader) {
+		if (loader != null) {
+			return ResourceBundle.getBundle(baseName, locale, loader);
+		} else {
+			return ResourceBundle.getBundle(baseName, locale);
+		}
+	}
+	
+	private static final FacesMessage getMessage(FacesContext context, String messageId, 
+			Object[] parameters, Locale locale) {
+		String summary = null;
+		String detail = null;
+		
+		ClassLoader loader = Thread.currentThread().getContextClassLoader();
+		
+		if (context != null) {
+			Application application = context.getApplication();
+			if (application != null) {
+				String messageBundleName = application.getMessageBundle();
+	
+				if (messageBundleName != null) {
+					ResourceBundle bundle = getResourceBundle(messageBundleName, locale, loader);
+					if (bundle != null) {
+						try {
+							summary = bundle.getString(messageId);
+							detail = bundle.getString(messageId + "_detail");
+						} catch (MissingResourceException e) {
+							//do nothing
+						}
+					}
+				}
+			}
+		}
+		
+		if (summary == null) {
+			ResourceBundle bundle = getResourceBundle(MESSAGES, locale, loader);
+			if (bundle != null) {
+				try {
+					summary = bundle.getString(messageId);
+					detail = bundle.getString(messageId + "_detail");
+				} catch (MissingResourceException e) {
+					//do nothing
+				}
+			}
+		}
+
+		if (summary == null) {
+			ResourceBundle bundle = getResourceBundle(FacesMessage.FACES_MESSAGES, locale, loader);
+			try {
+				summary = bundle.getString(messageId);
+				
+				if (summary == null) {
+					return null;
+				}
+
+				detail = bundle.getString(messageId + "_detail");
+			} catch (MissingResourceException e) {
+				//do nothing
+			}
+		}
+		
+		String formattedSummary = MessageFormat.format(summary, parameters);
+		String formattedDetail = null;
+		if (detail != null) {
+			formattedDetail = MessageFormat.format(detail, parameters);
+		}
+		
+		return new FacesMessage(formattedSummary, formattedDetail);
+	}
+	
+	public static final FacesMessage getMessage(FacesContext context, String messageId, 
+			Object[] parameters) {
+
+		Locale locale;
+		FacesMessage result = null;
+		
+		if (context != null) {
+			UIViewRoot viewRoot = context.getViewRoot();
+			if (viewRoot != null) {
+				locale = viewRoot.getLocale();
+			
+				if (locale != null) {
+					result = getMessage(context, messageId, parameters, locale);
+				}
+			}
+		}
+		
+		if (result == null) {
+			locale = Locale.getDefault();
+			result = getMessage(context, messageId, parameters, locale);
+		}
+
+		return result;
+	}
+	
+}

Added: trunk/sandbox/api/src/main/java/org/richfaces/model/DataProvider.java
===================================================================
--- trunk/sandbox/api/src/main/java/org/richfaces/model/DataProvider.java	                        (rev 0)
+++ trunk/sandbox/api/src/main/java/org/richfaces/model/DataProvider.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,50 @@
+/**
+ * 
+ */
+package org.richfaces.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * G3DataProvider is an interface that defines methods for manage loading data.<br>
+ * Usage:
+ * <pre>
+ * G3DataProvider&lt;SomeDataType&gt; dataProvider = new G3DataProvider&lt;SomeDataType&gt;();
+ * </pre>
+ * @author pawelgo
+ *
+ */
+public interface DataProvider<T> extends Serializable {
+	
+	/**
+	 * Get number of all rows.
+	 * @return number of rows.
+	 */
+	public int getRowCount();
+	
+	/**
+	 * Loads elements from given range.
+	 * Starting from startRow, and up to but excluding endRow. 
+	 * @param firstRow first row to load
+	 * @param endRow end row to load
+	 * @return element list
+	 */
+	public List<T> getItemsByRange(int firstRow, int endRow);
+	
+	/**
+	 * Load single element by given key.
+	 * @param key element key to be loaded.  
+	 * @return element or null, if not found 
+	 */
+	public T getItemByKey(Object key);
+	
+	/**
+	 * Get element key.
+	 * If key is not instance of Integer or org.richfaces.model.ScrollableTableDataModel.SimpleRowKey,
+	 * it is necessary to implement javax.faces.convert.Converter for key type.
+	 * @param item element, which key to be get 
+	 * @return element key
+	 */
+	public Object getKey(T item);
+}

Added: trunk/sandbox/api/src/main/java/org/richfaces/model/ExtendedDataTableModifiableModel.java
===================================================================
--- trunk/sandbox/api/src/main/java/org/richfaces/model/ExtendedDataTableModifiableModel.java	                        (rev 0)
+++ trunk/sandbox/api/src/main/java/org/richfaces/model/ExtendedDataTableModifiableModel.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,196 @@
+/**
+ * 
+ */
+package org.richfaces.model;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.faces.context.FacesContext;
+import javax.faces.model.DataModelListener;
+
+import org.ajax4jsf.model.DataVisitor;
+import org.ajax4jsf.model.ExtendedDataModel;
+import org.ajax4jsf.model.Range;
+import org.ajax4jsf.model.SequenceRange;
+import org.ajax4jsf.model.SerializableDataModel;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.richfaces.model.FilterField;
+import org.richfaces.model.SortField2;
+import org.richfaces.model.impl.expressive.JavaBeanWrapper;
+import org.richfaces.model.impl.expressive.ObjectWrapperFactory;
+import org.richfaces.model.impl.expressive.WrappedBeanComparator2;
+import org.richfaces.model.impl.expressive.WrappedBeanFilter;
+
+/**
+ * @author pawelgo
+ *
+ */
+public class ExtendedDataTableModifiableModel<T> extends ExtendedDataModel {
+
+	private static final Log log = LogFactory.getLog(ExtendedDataTableModifiableModel.class);
+
+	private ExtendedDataModel delegate;
+	
+	private ExtendedTableDataModel<T> originalModel;
+	
+	private ExtendedDataModel modifiedModel;
+
+	private String var;
+	
+	private List<FilterField> filterFields;
+	private List<SortField2> sortFields;
+	
+	/**
+	 * @param originalModel
+	 * @param var
+	 * @param filterFields
+	 * @param sortFields
+	 */
+	public ExtendedDataTableModifiableModel(ExtendedTableDataModel<T> originalModel,
+			String var, List<FilterField> filterFields,
+			List<SortField2> sortFields) {
+		this.originalModel = originalModel;
+		this.delegate = originalModel;
+		this.var = var;
+		this.filterFields = filterFields;
+		this.sortFields = sortFields;
+	}
+	
+	public void addDataModelListener(DataModelListener listener) {
+		originalModel.addDataModelListener(listener);
+	}
+
+	public DataModelListener[] getDataModelListeners() {
+		return originalModel.getDataModelListeners();
+	}
+
+	public int getRowCount() {
+		return delegate.getRowCount();
+	}
+
+	public Object getRowData() {
+		return delegate.getRowData();
+	}
+
+	public int getRowIndex() {
+		return delegate.getRowIndex();
+	}
+
+	public Object getRowKey() {
+		return delegate.getRowKey();
+	}
+
+	public SerializableDataModel getSerializableModel(Range range) {
+		return delegate.getSerializableModel(range);
+	}
+
+	public Object getWrappedData() {
+		return delegate.getWrappedData();
+	}
+
+	public boolean isRowAvailable() {
+		return delegate.isRowAvailable();
+	}
+
+	public void removeDataModelListener(DataModelListener listener) {
+		delegate.removeDataModelListener(listener);
+	}
+
+	public void setRowIndex(int rowIndex) {
+		delegate.setRowIndex(rowIndex);
+	}
+
+	public void setRowKey(Object key) {
+		delegate.setRowKey(key);
+	}
+
+	public void setWrappedData(Object data) {
+		delegate.setWrappedData(data);
+	}
+
+	@SuppressWarnings("unchecked")
+	public void walk(FacesContext context, DataVisitor visitor, Range range,
+			Object argument) throws IOException {
+		
+		if (shouldSort() || shouldFilter()) {
+			if (modifiedModel == null) {
+				modifiedModel = new ExtendedTableDataModelWrapper<T>(originalModel.getDataProvider(), (List<T>) prepareCollection());
+			}
+			delegate = modifiedModel;
+		} else {
+			delegate = originalModel;
+		}
+		
+		delegate.walk(context, visitor, range, argument);
+	}
+	
+	private boolean shouldSort() {
+		return sortFields != null && !sortFields.isEmpty();				
+	}
+	
+	private boolean shouldFilter() {
+		return filterFields != null && !filterFields.isEmpty();				
+	}
+	
+	private List<?> prepareCollection() {
+		int rowCount = originalModel.getRowCount();
+		final List<Object> collection;
+		
+		if (rowCount > 0) {
+			collection = new ArrayList<Object>(rowCount);
+		} else {
+			collection = new ArrayList<Object>();
+		}
+		
+		FacesContext context = FacesContext.getCurrentInstance();
+		try {
+		
+			originalModel.walk(context, new DataVisitor() {
+				public void process(FacesContext context, Object rowKey,
+						Object argument) throws IOException {
+					originalModel.setRowKey(rowKey);
+					if (originalModel.isRowAvailable()) {
+						collection.add(originalModel.getRowData());
+					}
+				}
+			}, new SequenceRange(0, -1),
+			null);
+		} catch (IOException e) {
+			log.error(e.getMessage(), e);
+		}
+		
+		List<Object> modifedcollection = collection;
+		
+		if (shouldFilter()) {
+			List <Object> filteredCollection = new ArrayList<Object>();
+			ObjectWrapperFactory wrapperFactory = new ObjectWrapperFactory(
+					context, var, filterFields);
+			WrappedBeanFilter wrappedBeanFilter = new WrappedBeanFilter(filterFields);
+			wrapperFactory.wrapList(modifedcollection);
+			for (Object object : modifedcollection) {
+				if(wrappedBeanFilter.accept((JavaBeanWrapper)object)) {
+					filteredCollection.add(object);
+				}
+			}
+			modifedcollection = filteredCollection;
+			wrapperFactory.unwrapList(modifedcollection);
+		}
+
+		if (shouldSort()) {
+			ObjectWrapperFactory wrapperFactory = new ObjectWrapperFactory(
+					context, var, sortFields);
+			WrappedBeanComparator2 wrappedBeanComparator = new WrappedBeanComparator2(
+					sortFields);
+			wrapperFactory.wrapList(modifedcollection);
+			Collections.sort(modifedcollection, wrappedBeanComparator);
+			wrapperFactory.unwrapList(modifedcollection);
+		}
+		return modifedcollection;
+ 		
+	}
+
+}

Added: trunk/sandbox/api/src/main/java/org/richfaces/model/ExtendedTableDataModel.java
===================================================================
--- trunk/sandbox/api/src/main/java/org/richfaces/model/ExtendedTableDataModel.java	                        (rev 0)
+++ trunk/sandbox/api/src/main/java/org/richfaces/model/ExtendedTableDataModel.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,353 @@
+/**
+ * 
+ */
+package org.richfaces.model;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.context.FacesContext;
+
+import org.ajax4jsf.model.DataVisitor;
+import org.ajax4jsf.model.Range;
+import org.ajax4jsf.model.SequenceRange;
+import org.ajax4jsf.model.SerializableDataModel;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Data model class for table components.<br> 
+ * Usage:
+ * <pre>
+ * G3TableDataModel&lt;SomeDataType&gt; dataModel = new G3TableDataModel&lt;SomeDataType&gt;(new G3DataProvider &lt;SomeDataType&gt;());
+ * </pre>
+ * @author pawelgo
+ *
+ */
+public class ExtendedTableDataModel<T> extends SerializableDataModel {
+
+//	/**
+//	 * Cache container for data presented in data model.
+//	 * It prevents load the same data many times.
+//	 * @author pawelgo
+//	 *
+//	 * @param <T> data type
+//	 */
+//	private static class DataCache<T> implements Serializable {
+//		private static final long serialVersionUID = 8409239905346930920L;
+//		private int startRow;
+//		private int endRow;
+//		//private SortOrder sortOrder;
+//		private List<T> loadedData;
+//
+//		public DataCache(int startRow, int endRow,
+//				List<T> loadedData) {
+//			super();
+//			this.startRow = startRow;
+//			this.endRow = endRow;
+//			//this.sortOrder = deepCopy(sortOrder);
+//			this.loadedData = loadedData;
+//		}
+//
+//		/**
+//		 * Returns a deep copy of an object
+//		 */
+//		@SuppressWarnings("unused")
+//		private SortOrder deepCopy(SortOrder sortOrder) {
+//			if (sortOrder == null)
+//				return null;
+//			SortOrder order = new SortOrder();
+//			if (sortOrder.getFields() != null) {
+//				SortField[] fields = new SortField[sortOrder.getFields().length];
+//				for (int i = 0; i < sortOrder.getFields().length; i++) {
+//					SortField f = sortOrder.getFields()[i];
+//					fields[i] = new SortField(f.getName(), f.getIndex(), f
+//							.getAscending());
+//				}//for
+//				order.setFields(fields);
+//			}//if
+//			return order;
+//		}//deepCopy
+//
+//		/**
+//		 * Returns a deep copy of an object using serialization.
+//		 */
+//		static public Object deepCopy(Object oldObj) throws Exception {
+//			ObjectOutputStream oos = null;
+//			ObjectInputStream ois = null;
+//			try {
+//				ByteArrayOutputStream bos = new ByteArrayOutputStream();
+//				oos = new ObjectOutputStream(bos);
+//				// serialize and pass the object
+//				oos.writeObject(oldObj);
+//				oos.flush();
+//				ByteArrayInputStream bin = new ByteArrayInputStream(bos
+//						.toByteArray());
+//				ois = new ObjectInputStream(bin);
+//				// return the new object
+//				return ois.readObject();
+//			} catch (Exception e) {
+//				if (log.isErrorEnabled())
+//					log.error("Exception in ObjectCloner = " + e);
+//				throw (e);
+//			} finally {
+//				oos.close();
+//				ois.close();
+//			}
+//		}//deepCopy
+//
+//		public boolean match(int s, int e) {
+//			return s == startRow && e == endRow;
+//			//&& sortOrdersMatch(sortOrder, this.sortOrder);
+//		}//match
+//
+//		@SuppressWarnings("unused")
+//		private boolean sortOrdersMatch(SortOrder sortOrder1,
+//				SortOrder sortOrder2) {
+//			boolean result = sortOrder1 == sortOrder2;
+//
+//			if (sortOrder1 != null && sortOrder2 != null) {
+//				result = sortOrder1.equals(sortOrder2);
+//			}
+//
+//			return result;
+//		}//sortOrdersMatch
+//
+//	}//DataCache
+
+	private static final long serialVersionUID = 7374505108088114161L;
+
+	private static final Log log = LogFactory.getLog(ExtendedTableDataModel.class);
+
+	private DataProvider<T> dataProvider;
+	private Object rowKey;
+	//private Map<Object,T> wrappedData = new HashMap<Object,T>();
+	private List<Object> wrappedKeys = null;
+	private boolean detached = false;
+	private Map<Object, T> wrappedData = new HashMap<Object, T>();
+
+	//private DataCache<T> dataCache;
+
+	public ExtendedTableDataModel(DataProvider<T> dataProvider) {
+		this.dataProvider = dataProvider;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.ajax4jsf.model.SerializableDataModel#update()
+	 */
+	public void update() {
+		;
+	}
+
+	/**
+	 * This method never called from framework.
+	 * (non-Javadoc)
+	 * @see org.ajax4jsf.model.ExtendedDataModel#getRowKey()
+	 */
+	public Object getRowKey() {
+		return rowKey;
+	}
+
+	/**
+	 * This method normally called by Visitor before request Data Row.
+	 * (non-Javadoc)
+	 * @see org.ajax4jsf.model.ExtendedDataModel#setRowKey(java.lang.Object)
+	 */
+	public void setRowKey(Object key) {
+		rowKey = key;
+	}
+
+	/** 
+	 * This is main part of Visitor pattern. Method called by framework many times during request processing. 
+	 * (non-Javadoc)
+	 * @see org.ajax4jsf.model.ExtendedDataModel#walk(javax.faces.context.FacesContext, org.ajax4jsf.model.DataVisitor, org.ajax4jsf.model.Range, java.lang.Object)
+	 */
+	public void walk(FacesContext context, DataVisitor visitor, Range range,
+			Object argument) throws IOException {
+		int rowC = getRowCount();
+		int firstRow = ((SequenceRange) range).getFirstRow();
+		int numberOfRows = ((SequenceRange) range).getRows();
+		if (numberOfRows <= 0) {
+			numberOfRows = rowC;
+		}
+		if (detached) { // Is this serialized model
+			// Here we just ignore current Rage and use whatever data was saved in serialized model. 
+			// Such approach uses much more getByPk() operations, instead of just one request by range.
+			// Concrete case may be different from that, so you can just load data from data provider by range.
+			// We are using wrappedKeys list only to preserve actual order of items.
+			for (Object key : wrappedKeys) {
+				setRowKey(key);
+				visitor.process(context, key, argument);
+			}
+		} else { // if not serialized, than we request data from data provider
+			wrappedKeys = new ArrayList<Object>();
+			int endRow = firstRow + numberOfRows;
+			if (endRow > rowC){
+				endRow = rowC; 
+			}
+			for (T item : loadData(firstRow, endRow)) {
+				Object key = getKey(item);
+				wrappedKeys.add(key);
+				wrappedData.put(key, item);
+				visitor.process(context, key, argument);
+			}
+		}
+	}//walk
+
+	/**
+	 * Load range of data items from the source.
+	 * Starting from startRow, and up to but excluding endRow
+	 * @param startRow
+	 * @param endRow
+	 * @return list of ordered data
+	 */
+	protected List<T> loadData(int startRow, int endRow) {
+		if (log.isDebugEnabled())
+			log.debug("load data from range: " + startRow + " - " + endRow);
+		if (startRow < 0){
+			startRow = 0;
+			throw new IllegalArgumentException("Illegal start index value: " + startRow);
+		}
+		int rowCount = getRowCount();
+		if (endRow > rowCount){
+			endRow = rowCount;
+			throw new IllegalArgumentException("Illegal end index value: " + endRow);
+		}
+		/*
+		if (dataCache == null || !dataCache.match(startRow, endRow)) {
+			if (log.isDebugEnabled())
+				log.debug("load and store in cache");
+			List<T> data = dataProvider.getItemsByRange(startRow, endRow);
+			dataCache = new DataCache<T>(startRow, endRow, data);
+		}
+		return dataCache.loadedData;
+		 */
+		//load all from provider and get sublist
+		return dataProvider.getItemsByRange(0, rowCount).subList(startRow, endRow);
+	}//loadData
+
+	/**
+	 * This method must return actual data rows count from the Data Provider. It is used by pagination control
+	 * to determine total number of data items.
+	 * (non-Javadoc)
+	 * @see javax.faces.model.DataModel#getRowCount()
+	 */
+	private Integer rowCount; // better to buffer row count locally
+
+	public int getRowCount() {
+		if (rowCount == null) {
+			rowCount = new Integer(dataProvider.getRowCount());
+		} else {
+			return rowCount.intValue();
+		}
+		return rowCount.intValue();
+		//return dataProvider.getRowCount();
+	}
+
+	/**
+	 *  This is main way to obtain data row. It is intensively used by framework. 
+	 * We strongly recommend use of local cache in that method. 
+	 * (non-Javadoc)
+	 * @see javax.faces.model.DataModel#getRowData()
+	 */
+	public T getRowData() {
+		if (rowKey == null) {
+			return null;
+		} else {
+			return  getObjectByKey(rowKey);
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	public Object getKey(T o) {
+		return dataProvider.getKey(o);
+	}
+
+	public T getObjectByKey(Object key) {
+		T t = wrappedData.get(key);
+		if (t == null){
+			t = dataProvider.getItemByKey(key);
+			wrappedData.put(key, t);
+		}
+		return t;
+	}
+
+	private Integer rowIndex;
+
+	/**
+	 * Unused rudiment from old JSF staff. (non-Javadoc)
+	 * 
+	 * @see javax.faces.model.DataModel#getRowIndex()
+	 */
+	public int getRowIndex() {
+		//throw new UnsupportedOperationException();
+		return rowIndex.intValue();
+	}
+
+	/**
+	 * Unused rudiment from old JSF staff.
+	 * (non-Javadoc)
+	 * @see javax.faces.model.DataModel#setRowIndex(int)
+	 */
+	public void setRowIndex(int rowIndex) {
+		//throw new UnsupportedOperationException();
+		this.rowIndex = rowIndex;
+	}
+
+	/**
+	 * Unused rudiment from old JSF staff.
+	 * (non-Javadoc)
+	 * @see javax.faces.model.DataModel#getWrappedData()
+	 */
+	public Object getWrappedData() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * Unused rudiment from old JSF staff.
+	 * (non-Javadoc)
+	 * @see javax.faces.model.DataModel#setWrappedData(java.lang.Object)
+	 */
+	public void setWrappedData(Object data) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * Never called by framework.
+	 * (non-Javadoc)
+	 * @see javax.faces.model.DataModel#isRowAvailable()
+	 */
+	public boolean isRowAvailable() {
+		return getRowData() != null;
+	}
+
+	/**
+	 * This method suppose to produce SerializableDataModel that will be
+	 * serialized into View State and used on a post-back. In current
+	 * implementation we just mark current model as serialized. In more
+	 * complicated cases we may need to transform data to actually serialized
+	 * form.
+	 */
+	public SerializableDataModel getSerializableModel(Range range) {
+		if (wrappedKeys != null) {
+			detached = true;
+			// Some activity to detach persistent data from wrappedData map may be taken here.
+			// In that specific case we are doing nothing.
+			return this;
+		} else {
+			return null;
+		}
+	}
+
+	public DataProvider<T> getDataProvider() {
+		return dataProvider;
+	}
+
+	public void setDataProvider(DataProvider<T> dataProvider) {
+		this.dataProvider = dataProvider;
+	}
+
+}

Added: trunk/sandbox/api/src/main/java/org/richfaces/model/ExtendedTableDataModelWrapper.java
===================================================================
--- trunk/sandbox/api/src/main/java/org/richfaces/model/ExtendedTableDataModelWrapper.java	                        (rev 0)
+++ trunk/sandbox/api/src/main/java/org/richfaces/model/ExtendedTableDataModelWrapper.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,40 @@
+/**
+ * 
+ */
+package org.richfaces.model;
+
+import java.util.List;
+
+/**
+ * @author pawelgo
+ *
+ */
+public class ExtendedTableDataModelWrapper<T> extends ExtendedTableDataModel<T> {
+
+	private static final long serialVersionUID = 6932958007982793632L;
+	
+	protected List<T> wrappedList;
+	
+	/**
+	 * 
+	 * @param dataProvider
+	 * @param wrappedList
+	 */
+	public ExtendedTableDataModelWrapper(DataProvider<T> dataProvider, List<T> wrappedList) {
+		super(dataProvider);
+		this.wrappedList = wrappedList;
+	}
+
+	protected List<T> loadData(int startRow, int endRow) {
+		//return super.loadData(startRow, endRow);
+		return wrappedList.subList(startRow, endRow);
+	}
+
+	public int getRowCount() {
+		return wrappedList.size();
+	}
+	
+	
+	
+
+}

Added: trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages.properties
===================================================================
--- trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages.properties	                        (rev 0)
+++ trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages.properties	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,10 @@
+# components
+org.richfaces.component.UIExtendedDataTable.Menu.Columns=Columns de
+org.richfaces.component.UIExtendedDataTable.Menu.SortAscending=Sort Ascending de
+org.richfaces.component.UIExtendedDataTable.Menu.SortDescending=Sort Descending de
+org.richfaces.component.UIExtendedDataTable.Menu.GroupByColumn=Group by this column de
+org.richfaces.component.UIExtendedDataTable.Menu.DisableGrouping=Disable Grouping de
+
+# converters
+
+# validators

Added: trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages_de.properties
===================================================================
--- trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages_de.properties	                        (rev 0)
+++ trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages_de.properties	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,10 @@
+# components
+org.richfaces.component.UIExtendedDataTable.Menu.Columns=Columns de
+org.richfaces.component.UIExtendedDataTable.Menu.SortAscending=Sort Ascending de
+org.richfaces.component.UIExtendedDataTable.Menu.SortDescending=Sort Descending de
+org.richfaces.component.UIExtendedDataTable.Menu.GroupByColumn=Group by this column de
+org.richfaces.component.UIExtendedDataTable.Menu.DisableGrouping=Disable Grouping de
+
+# converters
+
+# validators

Added: trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages_en.properties
===================================================================
--- trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages_en.properties	                        (rev 0)
+++ trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages_en.properties	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,10 @@
+# components
+org.richfaces.component.UIExtendedDataTable.Menu.Columns=Columns
+org.richfaces.component.UIExtendedDataTable.Menu.SortAscending=Sort Ascending
+org.richfaces.component.UIExtendedDataTable.Menu.SortDescending=Sort Descending
+org.richfaces.component.UIExtendedDataTable.Menu.GroupByColumn=Group by this column
+org.richfaces.component.UIExtendedDataTable.Menu.DisableGrouping=Disable Grouping
+
+# converters
+
+# validators

Added: trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages_pl.properties
===================================================================
--- trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages_pl.properties	                        (rev 0)
+++ trunk/sandbox/api/src/main/resources/org/richfaces/sandbox/messages/Messages_pl.properties	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,10 @@
+# components
+org.richfaces.component.UIExtendedDataTable.Menu.Columns=Kolumny
+org.richfaces.component.UIExtendedDataTable.Menu.SortAscending=Sortuj Rosn\u0105co
+org.richfaces.component.UIExtendedDataTable.Menu.SortDescending=Sortuj malej\u0105co
+org.richfaces.component.UIExtendedDataTable.Menu.GroupByColumn=Grupuj po kolumnie
+org.richfaces.component.UIExtendedDataTable.Menu.DisableGrouping=Wy\u0142\u0105cz grupowanie
+
+# converters
+
+# validators

Modified: trunk/sandbox/impl/pom.xml
===================================================================
--- trunk/sandbox/impl/pom.xml	2008-06-19 17:13:04 UTC (rev 9120)
+++ trunk/sandbox/impl/pom.xml	2008-06-19 19:21:31 UTC (rev 9121)
@@ -25,7 +25,12 @@
 		  <artifactId>richfaces-test</artifactId>
 		  <version>3.2.2-SNAPSHOT</version>
 		</dependency>
-
+		<dependency>
+			<groupId>javax.faces</groupId>
+			<artifactId>jsf-api</artifactId>
+			<version>1.2_07</version>
+			<scope>provided</scope>
+		</dependency> 
 	</dependencies>
 
 

Added: trunk/sandbox/impl/src/main/java/org/richfaces/component/util/SandboxMessageUtil.java
===================================================================
--- trunk/sandbox/impl/src/main/java/org/richfaces/component/util/SandboxMessageUtil.java	                        (rev 0)
+++ trunk/sandbox/impl/src/main/java/org/richfaces/component/util/SandboxMessageUtil.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,16 @@
+package org.richfaces.component.util;
+
+import javax.faces.application.FacesMessage;
+import javax.faces.context.FacesContext;
+
+
+public class SandboxMessageUtil extends AbstractMessageUtil {
+
+	private static final String MESSAGE_BUNDLE_NAME = "org.richfaces.sandbox.messages.Messages";
+
+	public static final FacesMessage getMessage(FacesContext context, String messageId, 
+			Object[] parameters) {
+
+		return AbstractMessageUtil.getMessage(context, messageId, parameters, MESSAGE_BUNDLE_NAME);
+	}
+}

Added: trunk/sandbox/ui/extendedDataTable/generatescript.xml
===================================================================
--- trunk/sandbox/ui/extendedDataTable/generatescript.xml	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/generatescript.xml	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<!-- ====================================================================== 
+     12.11.2005 23:40:43                                                        
+
+     assemblescripts    
+     Assemble all javaScript library files to one extended-data-table.js
+                   
+     maksim         
+                                                     
+     ====================================================================== -->
+
+<project name="assemblescripts" default="merge-scripts">
+	
+	<description>
+            Assemble all javaScript library files to one extended-data-table.js 
+    </description>
+		
+	<target name="merge-scripts-eclipse-3.3">
+				<property name="script-path" value="org/richfaces/renderkit/html/scripts"></property>
+				<property name="gen-script-name" value="extended-data-table.js"></property>	
+				<property name="gen-script-full-name" value="${target-dir}/${script-path}/${gen-script-name}"></property>
+				<concat append="false" binary="false"  destfile="${gen-script-full-name}">
+					<filelist dir="${resources-dir}">
+						<file name="${resources-dir}/ClientUILib.js"/>
+						<file name="${resources-dir}/ClientUI/common/utils/Utils.js"/>
+						<file name="${resources-dir}/ClientUI/common/utils/StringBuilder.js"/>
+						<file name="${resources-dir}/ClientUI/common/utils/Validators.js"/>
+						<file name="${resources-dir}/ClientUI/common/box/Box.js"/>
+						<file name="${resources-dir}/ClientUI/controls/datatable/ExtendedDataTable.js"/>
+						<file name="${resources-dir}/ClientUI/controls/datatable/ExtendedDataTableHeader.js"/>
+						<file name="${resources-dir}/ClientUI/controls/datatable/ExtendedDataTableSelection.js"/>
+					</filelist>
+				</concat>
+			</target>
+	
+	
+		<target name="merge-scripts">
+			<property name="script-path" value="org/richfaces/renderkit/html/scripts"></property>
+			<property name="gen-script-name" value="extended-data-table.js"></property>	
+			<property name="gen-script-full-name" value="${target-dir}/${script-path}/${gen-script-name}"></property>
+			<concat append="false" binary="false"  destfile="${gen-script-full-name}">
+				<filelist dir="${resources-dir}">
+					<file name="ClientUILib.js"/>
+					<file name="/ClientUI/common/utils/Utils.js"/>
+					<file name="/ClientUI/common/utils/StringBuilder.js"/>
+					<file name="/ClientUI/common/utils/Validators.js"/>
+					<file name="/ClientUI/common/box/Box.js"/>
+					<file name="/ClientUI/controls/dataTable/ExtendedDataTable.js"/>
+					<file name="/ClientUI/controls/datatable/ExtendedDataTableHeader.js"/>
+					<file name="/ClientUI/controls/datatable/ExtendedDataTableSelection.js"/>
+				</filelist>
+			</concat>
+		</target>
+</project>
+

Added: trunk/sandbox/ui/extendedDataTable/pom.xml
===================================================================
--- trunk/sandbox/ui/extendedDataTable/pom.xml	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/pom.xml	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,80 @@
+<?xml version="1.0"?><project>
+  <parent>
+	<artifactId>ui</artifactId>
+	<groupId>org.richfaces.sandbox</groupId>
+	<version>3.2.2-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.richfaces.sandbox.ui</groupId>
+  <artifactId>extendedDataTable</artifactId>
+  <name>extendedDataTable</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.richfaces.cdk</groupId>
+        <artifactId>maven-cdk-plugin</artifactId>
+        <version>3.2.2-SNAPSHOT</version>
+        <executions>
+          <execution>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>generate</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <library>
+            <prefix>org.richfaces.sandbox</prefix>
+            <taglib>
+              <shortName>extendedDataTable</shortName>
+            </taglib>
+          </library>
+        </configuration>
+      </plugin>
+    	
+  	  <plugin>
+  		<artifactId>maven-antrun-plugin</artifactId>
+  		<executions>
+  			<execution>
+  				<phase>generate-resources</phase>
+  				<goals>
+  					<goal>run</goal>
+  				</goals>
+  				<configuration>
+					<tasks>
+						<ant antfile="${basedir}/generatescript.xml" inheritRefs="true">
+							<target name="merge-scripts" />
+							<property name="target-dir" value="${project.build.directory}/classes">
+							</property>
+							<property name="resources-dir" value="${basedir}/src/main/javascript">
+							</property>
+						</ant>
+					</tasks>
+					<resourceRoot>
+						${project.build.directory}/generated-component/resources
+					</resourceRoot>
+  				</configuration>
+  			</execution>
+  		</executions>
+  	  </plugin>    	
+    	
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.richfaces.framework</groupId>
+      <artifactId>richfaces-impl</artifactId>
+      <version>3.2.2-SNAPSHOT</version>
+    </dependency>
+	<dependency>
+      <groupId>org.richfaces.ui</groupId>
+      <artifactId>richfaces-ui</artifactId>
+      <version>3.2.2-SNAPSHOT</version>
+    </dependency>
+	<dependency>
+	  <groupId>org.richfaces.sandbox</groupId>
+	  <artifactId>richfaces-sandbox-api</artifactId>
+	  <version>3.2.2-SNAPSHOT</version>
+  	</dependency>
+  </dependencies>
+</project>
\ No newline at end of file

Added: trunk/sandbox/ui/extendedDataTable/src/main/config/component/ExtendedDataTable.xml
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/config/component/ExtendedDataTable.xml	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/config/component/ExtendedDataTable.xml	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE components PUBLIC "-//AJAX4JSF//CDK Generator config/EN"  "http://labs.jboss.com/jbossrichfaces/component-config.dtd">
+<components>
+	<component>
+		<name>org.richfaces.ExtendedDataTable</name>
+		<family>org.richfaces.ExtendedDataTable</family>
+		<classname>org.richfaces.component.html.HtmlExtendedDataTable</classname>
+		<superclass>org.richfaces.component.UIExtendedDataTable</superclass>
+		<description>
+			<![CDATA[
+    ]]>
+		</description>
+		<renderer generate="true" override="true">
+			<name>org.richfaces.ExtendedDataTableRenderer</name>
+			<template>org/richfaces/htmlExtendedDataTable.jspx</template>
+		</renderer>
+		<tag>
+			<name>extendedDataTable</name>
+			<classname>org.richfaces.taglib.ExtendedDataTableTag</classname>
+			<superclass>
+				org.ajax4jsf.webapp.taglib.HtmlComponentTagBase
+			</superclass>
+		</tag>
+		<!--
+		<taghandler>
+			<classname>org.ajax4jsf.tag.TestHandler</classname>
+		</taghandler>
+		-->
+		&ui_component_attributes;
+		&ui_data_attributes;
+		&html_universal_attributes;
+		&html_table_attributes;
+		&html_events;
+		&spec_table_attributes;
+		<property>
+			<name>onselectionchange</name>
+			<classname>java.lang.String</classname>
+			<description>
+			HTML: script expression to invoke on changing of rows selection 
+			</description>
+		</property>
+		<property>
+        <name>selectionMode</name>
+        <classname>java.lang.String</classname>
+        <description>
+            single [default]: Single row can be selected.
+            multi: Multiple rows can be selected.
+            none: no rows can be selected. 
+        </description>
+        <defaultvalue>
+        	"single"
+        </defaultvalue>
+    </property>
+		<property>
+			<name>height</name>
+			<classname>java.lang.String</classname>
+			<description>Defines a height of the component. Default value is 500px</description>
+			<defaultvalue>"500px"</defaultvalue>
+		</property>	
+		<property>
+			<name>selectedClass</name>
+			<classname>java.lang.String</classname>
+			<description>
+			CSS class for selected rows
+			</description>
+		</property>
+		<property>
+			<name>activeClass</name>
+			<classname>java.lang.String</classname>
+			<description>
+				CSS class for active row
+			</description>
+		</property>
+		
+		<property>
+			<name>ajaxKeys</name>
+			<classname>java.util.Set</classname>
+			<description>This attribute defines row keys that are updated after an AJAX request</description>
+		</property>
+		<property>
+			<name>border</name>
+			<classname>java.lang.String</classname>
+			<description>This attributes specifies the width of the frame around a component</description>
+			<defaultvalue>"0"</defaultvalue>
+		</property>
+		<property>
+			<name>height</name>
+			<classname>java.lang.String</classname>
+			<description>This attributes specifies the height of the component</description>
+			<defaultvalue>"500"</defaultvalue>
+		</property>		
+		<property>
+			<name>cellpadding</name>
+			<classname>java.lang.String</classname>
+			<description>This attribute specifies the amount of space between the border of the cell and its contents</description>
+			<defaultvalue>"0"</defaultvalue>
+		</property>
+		<property>
+			<name>cellspacing</name>
+			<classname>java.lang.String</classname>
+			<description>This attribute specifies the amount of space between the border of the cell and its contents.  The attribute also specifies the amount of space to leave between cells
+			</description>
+			<defaultvalue>"0"</defaultvalue>
+		</property>
+		<property>
+			<name>captionClass</name>
+			<classname>java.lang.String</classname>
+			<description>Space-separated list of CSS style class(es) that are be applied to caption for this component</description>
+			<defaultvalue>""</defaultvalue>
+		</property>
+		<property>
+			<name>captionStyle</name>
+			<classname>java.lang.String</classname>
+			<description>CSS style(s) is/are to be applied to caption when this component is rendered</description>
+		</property>
+		<property>
+			<name>headerClass</name>
+			<classname>java.lang.String</classname>
+			<description>Space-separated list of CSS style class(es) that are be applied to header for this component
+			</description>
+			<defaultvalue>""</defaultvalue>
+		</property>
+		<property>
+			<name>footerClass</name>
+			<classname>java.lang.String</classname>
+			<description>Space-separated list of CSS style class(es) that are be applied to footer for this component
+			</description>
+			<defaultvalue>""</defaultvalue>
+		</property>
+	
+		<property>
+			<name>componentState</name>
+			<classname>org.ajax4jsf.model.DataComponentState</classname>
+			<description>It defines EL-binding  for a component state for saving or redefinition</description>
+		</property>		
+		<property hidden="true" existintag="false" exist="false" >
+			<name>rowKey</name>
+			<classname>java.lang.Object</classname>
+			<description>
+				RowKey is a representation of an identifier for a specific data row
+			</description>
+		</property>
+		<property>
+			<name>rowKeyVar</name>
+			<classname>java.lang.String</classname>
+			<description>The attribute provides access to a row key in a Request scope</description>
+		</property>
+		<property>
+			<name>stateVar</name>
+			<classname>java.lang.String</classname>
+			<description>
+				The attribute  provides access to a component state on the client side
+			</description>
+		</property>	
+		<property>
+			<name>value</name>
+			<classname>java.lang.Object</classname>
+			<description>
+				The current value for this component
+			</description>
+		</property>
+		<!-- rows javascript events -->
+		<property>
+			<name>onRowClick</name>
+			<classname>java.lang.String</classname>
+			<description>
+				HTML: a script expression; a pointer button is clicked on row
+			</description>
+		</property>
+		<property>
+			<name>onRowDblClick</name>
+			<classname>java.lang.String</classname>
+			<description>HTML: a script expression; a pointer button is double-clicked on row
+			</description>
+		</property>
+		<property>
+			<name>onRowMouseUp</name>
+			<classname>java.lang.String</classname>
+			<description>HTML: script expression; a pointer button is released on row
+			</description>
+		</property>
+		<property>
+			<name>onRowMouseDown</name>
+			<classname>java.lang.String</classname>
+			<description>HTML: script expression; a pointer button is pressed down on row
+			</description>
+		</property>
+		<property>
+			<name>onRowMouseOver</name>
+			<classname>java.lang.String</classname>
+			<description>HTML: a script expression; a pointer is moved onto of row
+			</description>
+		</property>
+		<property>
+			<name>onRowMouseOut</name>
+			<classname>java.lang.String</classname>
+			<description>HTML: a script expression; a pointer is moved away of row
+			</description>
+		</property>
+		<property>
+			<name>onRowMouseMove</name>
+			<classname>java.lang.String</classname>
+			<description>HTML: a script expression; a pointer is moved within of row
+			</description>
+		</property>
+		<property>
+			<name>sortPriority</name>
+			<classname>java.util.Collection</classname>
+			<description>Defines a set of column ids in the order the columns could be set</description>
+		</property>
+		<property hidden="true" existintag="false" exist="false" >
+			<name>sortFields</name>
+		</property>
+		<property hidden="true" existintag="false" exist="false" >
+			<name>filterFields</name>
+		</property>
+		<property>
+			<name>sortMode</name>
+			<classname>java.lang.String</classname>
+			<description>
+			Defines mode of sorting. Possible values are 'single' for sorting of one column and 'multi' for some.
+			</description>
+		</property>
+		<property hidden="true" existintag="false" exist="false" >
+			<name>summary</name>
+			<classname>java.lang.Object</classname>
+		</property>
+		
+		<property elonly="true">
+			<name>tableState</name>
+			<classname>java.lang.String</classname>
+			<description>
+			ValueBinding pointing at a property of a String to hold table state
+			</description>
+		</property>
+		
+		<property attachedstate="true" elonly="true">
+			<name>selection</name>
+			<classname>org.richfaces.model.selection.Selection</classname>
+			<description>Value binding representing selected rows</description>
+		</property>
+		
+	</component>
+</components>

Added: trunk/sandbox/ui/extendedDataTable/src/main/config/component/README
===================================================================

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/ExtendedTableColumnsIterator.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/ExtendedTableColumnsIterator.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/ExtendedTableColumnsIterator.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,77 @@
+/**
+ * 
+ */
+package org.richfaces.component;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import javax.faces.component.UIComponent;
+
+import org.richfaces.component.UIColumn;
+
+/**
+ * Columns iterator that work exactly like org.richfaces.component.ColumnsIterator
+ * but raw type is {@link org.richfaces.component.UIColumn} instead of {@link javax.faces.component.UIComponent}
+ * @see org.richfaces.component.ColumnsIterator
+ * @author pawelgo
+ *
+ */
+public class ExtendedTableColumnsIterator implements Iterator<UIColumn> {
+	
+	protected Iterator<UIComponent> childrenIterator;
+	protected UIColumn next;
+	protected boolean initialized = false;
+	
+	/**
+	 * Creates iterator for table.
+	 * @param dataTable table for which iterator is created
+	 */
+	public ExtendedTableColumnsIterator(UIExtendedDataTable dataTable) {
+		super();
+		childrenIterator = dataTable.getChildren().iterator();
+	}
+
+	/* (non-Javadoc)
+	 * @see java.util.Iterator#hasNext()
+	 */
+	public boolean hasNext() {
+		if(!initialized){
+			next = nextColumn();
+			initialized = true;
+		}
+		return null != next;
+	}
+
+	/* (non-Javadoc)
+	 * @see java.util.Iterator#next()
+	 */
+	public UIColumn next() {
+		if (!hasNext()) {
+			throw new NoSuchElementException();
+		}
+		UIColumn result = next;
+		next = nextColumn();
+		return result;
+	}
+	
+	protected UIColumn nextColumn(){
+		UIColumn nextColumn = null;
+		while (childrenIterator.hasNext()) {
+			UIComponent child = childrenIterator.next();
+			if(child instanceof UIColumn){
+				nextColumn = (UIColumn)child;
+				break;
+			}
+		}
+		return nextColumn;
+	}
+
+	/* (non-Javadoc)
+	 * @see java.util.Iterator#remove()
+	 */
+	public void remove() {
+		throw new UnsupportedOperationException("Iterator is read-only");
+	}
+
+}

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/README
===================================================================

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/SortedColumnsIterator.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/SortedColumnsIterator.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/SortedColumnsIterator.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,26 @@
+/**
+ * 
+ */
+package org.richfaces.component;
+
+/**
+ * Columns iterator that returns column in sort order.
+ * 
+ * @see UIExtendedDataTable#getSortedChildren()
+ * @author pawelgo
+ * 
+ */
+public class SortedColumnsIterator extends ExtendedTableColumnsIterator {
+
+    /**
+     * Creates iterator for table.
+     * 
+     * @param dataTable
+     *            table for which iterator is created
+     */
+    public SortedColumnsIterator(UIExtendedDataTable dataTable) {
+        super(dataTable);
+        childrenIterator = dataTable.getSortedChildren().iterator();
+    }
+
+}

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/UIExtendedDataTable.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/UIExtendedDataTable.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/UIExtendedDataTable.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,283 @@
+/**
+ *
+ */
+
+package org.richfaces.component;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.FacesEvent;
+import javax.faces.event.PhaseId;
+
+import org.ajax4jsf.event.AjaxEvent;
+import org.ajax4jsf.model.ExtendedDataModel;
+import org.richfaces.event.ChangeColumnVisibilityEvent;
+import org.richfaces.event.ColumnResizeEvent;
+import org.richfaces.event.DragDropEvent;
+import org.richfaces.model.ExtendedDataTableModifiableModel;
+import org.richfaces.model.ExtendedTableDataModel;
+import org.richfaces.model.FilterField;
+import org.richfaces.model.ModifiableModel;
+import org.richfaces.model.SortField2;
+
+/**
+ * JSF component class
+ * 
+ */
+public abstract class UIExtendedDataTable extends UIDataTable implements
+        Selectable {
+    
+    /**
+     * COMPONENT_TYPE
+     */
+    public static final String COMPONENT_TYPE = "org.richfaces.ExtendedDataTable";
+    
+    /**
+     * COMPONENT_FAMILY
+     */
+    public static final String COMPONENT_FAMILY = "org.richfaces.ExtendedDataTable";
+
+    protected UIExtendedDataTableState state;
+
+    public abstract Object getActiveRowKey();
+
+    public abstract void setActiveRowKey(Object activeRowKey);
+
+    public void broadcast(FacesEvent event) throws AbortProcessingException {
+        super.broadcast(event);
+        if (event instanceof DragDropEvent) {
+            processDradDrop((DragDropEvent) event);
+        } else if (event instanceof ChangeColumnVisibilityEvent) {
+            processChangeColumnVisibility((ChangeColumnVisibilityEvent) event);
+        } else if (event instanceof ColumnResizeEvent) {
+            processColumnResize((ColumnResizeEvent) event);
+        }
+
+    }
+
+    public void queueEvent(FacesEvent event) {
+        if (event instanceof DragDropEvent) {
+            new AjaxEvent(this).queue();
+            event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
+            // event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
+            // new AjaxEvent(this).queue();
+        } else if (event instanceof ChangeColumnVisibilityEvent) {
+            new AjaxEvent(this).queue();
+            event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
+        } else if (event instanceof ColumnResizeEvent) {
+            new AjaxEvent(this).queue();
+            event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
+        }
+        super.queueEvent(event);
+    }
+
+    public Iterator<UIColumn> getSortedColumns() {
+        return new SortedColumnsIterator(this);
+    }
+
+    public Iterator<UIColumn> getChildColumns() {
+        return new ExtendedTableColumnsIterator(this);
+    }
+
+    public void processDradDrop(DragDropEvent event) {
+        String dragValue = event.getDragValue().toString();// dnd_drag_script
+        String dropValue = event.getDropValue().toString();// dnd_drop_script
+
+        ensureTableStateInitialized();
+        state.changeColumnsOrder(dragValue, dropValue, event.isDropBefore());
+        state.publishChanges(getFacesContext(), this);
+
+        getFacesContext().renderResponse();
+    }
+
+    public List<UIComponent> getSortedChildren() {
+        ensureTableStateInitialized();
+        return state.sortColumns(getFacesContext(), super.getChildren());
+    }
+
+    public void ensureTableStateInitialized() {
+        if (state == null) {
+            state = UIExtendedDataTableState.getUIExtendedDataTableState(this);
+        }
+    }
+
+    public void processChangeColumnVisibility(ChangeColumnVisibilityEvent event) {
+        ensureTableStateInitialized();
+        state.toggleColumnVisibility(this, event.getColumnId());
+        state.publishChanges(getFacesContext(), this);
+
+        getFacesContext().renderResponse();
+    }// processChangeColumnVisibility
+
+    public boolean isColumnVisible(UIComponent column) {
+        ensureTableStateInitialized();
+        return state.isColumnVisible(column.getId());
+    }// isColumnVisible
+
+    public void processColumnResize(ColumnResizeEvent event) {
+        ensureTableStateInitialized();
+        state.changeColumnSize(this, event.getColumnWidths());
+        state.publishChanges(getFacesContext(), this);
+
+        getFacesContext().renderResponse();
+    }// processChangeColumnVisibility
+
+    public String getColumnSize(UIComponent column) {
+        ensureTableStateInitialized();
+        return state.getColumnSize(column);
+    }
+
+    public boolean isGroupingOn() {
+        ensureTableStateInitialized();
+        return state.isGroupingOn();
+    }
+
+    public String getGroupingColumnId() {
+        ensureTableStateInitialized();
+        return state.getGroupingColumnId();
+    }
+
+    public UIColumn getGroupingColumn() {
+        String groupingColumnId = getGroupingColumnId();
+        if (groupingColumnId == null)
+            return null;
+        for (Iterator<UIColumn> columns = getChildColumns(); columns.hasNext();) {
+            UIColumn child = columns.next();
+            if (groupingColumnId.equalsIgnoreCase(child.getId())) {
+                return child;
+            }
+        }
+        return null;
+    }// getGroupingColumn
+
+    public void setGroupingColumn(org.richfaces.component.UIColumn column) {
+        ensureTableStateInitialized();
+        if (column == null) {
+            state.disableGrouping();
+        } else {
+            state.groupBy(column.getId(), column.getSortOrder());
+        }
+        state.publishChanges(getFacesContext(), this);
+    }
+
+    public void resetGroupVisibilityState() {
+        ensureTableStateInitialized();
+        state.resetGroupVisibilityState();
+    }
+
+    public void disableGrouping() {
+        ensureTableStateInitialized();
+        state.disableGrouping();
+        state.publishChanges(getFacesContext(), this);
+    }
+
+    public boolean groupIsExpanded(int index) {
+        ensureTableStateInitialized();
+        return state.groupIsExpanded(index);
+    }
+
+    public void toggleGroup(int index) {
+        ensureTableStateInitialized();
+        state.toggleGroup(index);
+        state.publishChanges(getFacesContext(), this);
+    }
+
+    public Collection<Object> getSortPriority() {
+        Collection<Object> priority = super.getSortPriority();
+        if (isGroupingOn()) {// grouping is on
+            String groupColId = getGroupingColumnId();
+            // try to add group column id as first
+            if (priority.contains(groupColId)) {
+                priority.remove(groupColId);
+            }
+            if (priority instanceof List) {
+                ((List<Object>) priority).add(0, groupColId);
+            } else {
+                priority.add(groupColId);
+            }
+        }
+        return priority;
+    }
+
+    public Object saveState(FacesContext context) {
+        Object values[] = new Object[2];
+        values[0] = super.saveState(context);
+        values[1] = state;
+        return values;
+    }
+
+    public void restoreState(FacesContext context, Object state) {
+        Object values[] = (Object[]) state;
+        super.restoreState(context, values[0]);
+        this.state = (UIExtendedDataTableState) values[1];
+    }
+
+    public int getVisibleColumnsCount() {
+        int count = 0;
+        for (Iterator<UIColumn> iter = getChildColumns(); iter.hasNext();) {
+            UIColumn column = iter.next();
+            if (column.isRendered())
+                count++;
+        }// for
+        return count;
+    }// getVisibleColumnnCount
+
+    @SuppressWarnings("unchecked")
+    protected ExtendedDataModel createDataModel() {
+        List<FilterField> filterFields = new LinkedList<FilterField>();
+        Map<String, SortField2> sortFieldsMap = new LinkedHashMap<String, SortField2>();
+        List<UIComponent> list = getChildren();
+        for (Iterator<UIComponent> iterator = list.iterator(); iterator
+                .hasNext();) {
+            UIComponent component = iterator.next();
+            if (component instanceof org.richfaces.component.UIColumn) {
+                org.richfaces.component.UIColumn column = (org.richfaces.component.UIColumn) component;
+                FilterField filterField = column.getFilterField();
+                if (filterField != null) {
+                    filterFields.add(filterField);
+                }
+                SortField2 sortField = column.getSortField();
+                if (sortField != null) {
+                    sortFieldsMap.put(component.getId(), sortField);
+                }
+            }
+
+        }
+        List<SortField2> sortFields = new LinkedList<SortField2>();
+        Collection<Object> sortPriority = getSortPriority();
+        if (sortPriority != null) {
+            for (Object object : sortPriority) {
+                if (object instanceof String) {
+                    String id = (String) object;
+                    SortField2 sortField = sortFieldsMap.get(id);
+                    if (sortField != null) {
+                        sortFields.add(sortField);
+                        sortFieldsMap.remove(id);
+                    }
+                }
+            }
+        }
+        sortFields.addAll(sortFieldsMap.values());
+        setFilterFields(filterFields);
+        setSortFields(sortFields);
+        ExtendedDataModel dataModel = (ExtendedDataModel) super.getDataModel();
+        if (dataModel instanceof ExtendedTableDataModel<?>) {
+        	ExtendedTableDataModel<?> tableDataModel = (ExtendedTableDataModel<?>) dataModel;
+            return new ExtendedDataTableModifiableModel(tableDataModel, getVar(),
+                    getFilterFields(), getSortFields());
+        } else {
+            ModifiableModel modifiableModel = new ModifiableModel(dataModel, getVar());
+            modifiableModel.modify(getFilterFields(), getSortFields());
+            
+            return modifiableModel;
+        }
+    }
+}

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/UIExtendedDataTableState.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/UIExtendedDataTableState.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/component/UIExtendedDataTableState.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,700 @@
+/**
+ * 
+ */
+package org.richfaces.component;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.el.ValueExpression;
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+import org.richfaces.model.Ordering;
+
+/**
+ * @author pawelgo
+ *
+ */
+public class UIExtendedDataTableState implements Serializable {
+
+	private static final long serialVersionUID = -3103664821855261335L;
+
+	public static final String TABLE_STATE_ATTR_NAME = "tableState";
+	
+	protected static final String SEP = ":";
+	
+	protected ColumnsOrder columnsOrder;
+	protected ColumnsVisibility columnsVisibility;
+	protected ColumnsSizeState columnsSizeState;
+	protected ColumnGroupingState columnGroupingState;
+	
+	public static UIExtendedDataTableState getUIExtendedDataTableState(UIExtendedDataTable extendedDataTable){
+		UIExtendedDataTableState state = new UIExtendedDataTableState();
+		state.init(extendedDataTable);
+		return state;
+	}//init
+	
+	/**
+	 * Converts its state based on table attribute value or create default state if it is not set.
+	 */
+	protected void init(UIExtendedDataTable extendedDataTable){
+		//get state value from components attributes
+		String value = (String)extendedDataTable.getAttributes().get(TABLE_STATE_ATTR_NAME);
+		//split state value into parts
+		String[] values = fromString(value);
+		//initialize columns order part
+		String val = (values != null && values.length>0) ? values[0] : null;
+		columnsOrder = ColumnsOrder.getColumnsOrder(extendedDataTable, val);
+		//initialize columns visibility part
+		val = (values != null && values.length>1) ? values[1] : null;
+		columnsVisibility = ColumnsVisibility.getColumnsVisibility(extendedDataTable, val);
+		//initialize columns size part
+		val = (values != null && values.length>2) ? values[2] : null;
+		columnsSizeState = ColumnsSizeState.getColumnsSize(extendedDataTable, val);
+		//initialize column grouping part
+		val = (values != null && values.length>3) ? values[3] : null;
+		columnGroupingState = ColumnGroupingState.getColumnGropingState(extendedDataTable, val);
+	}//init
+	
+	/**
+	 * Puts own state into component state. 
+	 */
+	public void publishChanges(FacesContext context, UIExtendedDataTable extendedDataTable){
+		ValueExpression ve = extendedDataTable.getValueExpression(TABLE_STATE_ATTR_NAME);
+		if ((null != ve) && (!ve.isReadOnly(context.getELContext()))) {
+			ve.setValue(context.getELContext(), toString());
+		}
+	}//publishChanges
+	
+	/**
+	 * Converts its state to String representation.
+	 */
+	public String toString(){
+		String[] values = new String[4];
+		values[0] = columnsOrder.toString();
+		values[1] = columnsVisibility.toString();
+		values[2] = columnsSizeState.toString();
+		values[3] = columnGroupingState.toString();
+		StringBuilder builder = new StringBuilder();
+		for (String str : values){
+			builder.append(str).append(SEP);
+		}//for
+		return builder.toString();
+	}//toString
+	
+	public String[] fromString(String value){
+		return (value == null) ? null : value.split(SEP);
+	}//fromString
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see ColumnsOrder#changeOrder(String, String)
+	 */
+	public void changeColumnsOrder(String sourceColumnId, String targetColumnId, boolean dropBefore) {
+		columnsOrder.changeOrder(sourceColumnId, targetColumnId, dropBefore);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see ColumnsOrder#sortColumns(FacesContext, List)
+	 */
+	public List<UIComponent> sortColumns(FacesContext context,
+			List<UIComponent> children) {
+		return columnsOrder.sortColumns(context, children);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see ColumnsVisibility#isVisible(String)
+	 */
+	public boolean isColumnVisible(String columnId) {
+		return columnsVisibility.isVisible(columnId);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see ColumnsVisibility#toggleVisibility(UIExtendedDataTable, String)
+	 */
+	public void toggleColumnVisibility(UIExtendedDataTable extendedDataTable,
+			String columnId) {
+		columnsVisibility.toggleVisibility(extendedDataTable, columnId);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see ColumnsSizeState#changeColumnSize(UIExtendedDataTable, String)
+	 */
+	public void changeColumnSize(UIExtendedDataTable extendedDataTable,
+			String newValue) {
+		columnsSizeState.changeColumnSize(extendedDataTable, newValue);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see ColumnsSizeState#getColumnSize(UIComponent)
+	 */
+	public String getColumnSize(UIComponent column) {
+		return columnsSizeState.getColumnSize(column);
+	}
+	
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see ColumnGroupingState#isGroupingOn()
+	 */
+	public boolean isGroupingOn(){
+		return columnGroupingState.isGroupingOn();
+	}
+	
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see ColumnGroupingState#getGroupingColumnId()
+	 */
+	public String getGroupingColumnId(){
+		return columnGroupingState.getGroupingColumnId();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see ColumnGroupingState#groupBy(String, Ordering)
+	 */
+	public void groupBy(String colId, Ordering ordering) {
+		columnGroupingState.groupBy(colId, ordering);
+	}
+	
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see ColumnGroupingState#resetGroupVisibilityState()
+	 */
+	public void resetGroupVisibilityState(){
+		columnGroupingState.resetGroupVisibilityState();
+	}
+	
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see ColumnGroupingState#disableGrouping()
+	 */
+	public void disableGrouping(){
+		columnGroupingState.disableGrouping();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see ColumnGroupingState#groupIsExpanded(int)
+	 */
+	public boolean groupIsExpanded(int index) {
+		return columnGroupingState.groupIsExpanded(index);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see ColumnGroupingState#toggleGroup(int)
+	 */
+	public void toggleGroup(int index) {
+		columnGroupingState.toggleGroup(index);
+	}
+	
+	
+}
+
+class ColumnsSizeState implements Serializable{
+
+	private static final long serialVersionUID = 8724163192351491340L;
+
+	private static final String SEP = ";";
+	
+	private static final String DEFAULT_WIDTH = "100";
+	
+	private String value;
+
+	private ColumnsSizeState() {
+		super();
+	}
+	
+	static ColumnsSizeState getColumnsSize(UIExtendedDataTable extendedDataTable, String val){
+		ColumnsSizeState columnsSize = new ColumnsSizeState();
+		columnsSize.init(extendedDataTable, val);
+		return columnsSize;
+	}
+	
+	/**
+	 * Converts its state from String representation or create default state if it is not set.
+	 */
+	private void init(UIExtendedDataTable extendedDataTable, String val){
+		value = val;
+		if ((value == null) || (value.length() == 0))
+			createDefaultColumnsSizeState(extendedDataTable);
+	}
+	
+	/**
+	 * Converts its state to String representation.
+	 */
+	public String toString(){
+		return value;
+	}
+	
+	/**
+	 * Create default column order based on component children.
+	 */
+	private void createDefaultColumnsSizeState(UIExtendedDataTable extendedDataTable){
+		StringBuilder builder = new StringBuilder();
+		
+		for (Iterator<UIColumn> iter = extendedDataTable.getChildColumns(); iter.hasNext();) {
+			UIColumn col = iter.next();
+			builder.append(col.getId().toUpperCase()).append("-").append(getDefaultColumnSize(col)).append(SEP);
+		}
+		value = builder.toString();
+	}
+	
+	private String getDefaultColumnSize(UIComponent column){
+		String widthStr = (String) column.getAttributes().get("width");
+		return formatWidth((widthStr == null || widthStr.length() == 0) ? DEFAULT_WIDTH : widthStr);
+	}
+	
+	public String getColumnSize(UIComponent column){
+		if (value == null)
+			return getDefaultColumnSize(column);
+		String[] widths = value.split(SEP);
+		if (widths != null){
+			String colId = column.getId().toUpperCase();
+			for (String val : widths){
+				if (val.toUpperCase().startsWith(colId+"-")){
+					return  formatWidth(val.split("-")[1]);
+				}
+			}//for
+		}
+		return getDefaultColumnSize(column);		
+	}
+	
+	private String formatWidth(String value){
+		if(value.endsWith("px"))
+			return value.substring(0,value.indexOf("px"));
+		return value; 
+	}
+	
+	public void changeColumnSize(UIExtendedDataTable extendedDataTable, String newValue){
+		if (value == null)
+			return;
+		Set<String> widths = new HashSet<String>(Arrays.asList(value.toUpperCase().split(SEP)));
+		String[] newWidths = newValue.split(";");
+		int index = 0;
+		for (Iterator<UIColumn> iter = extendedDataTable.getSortedColumns(); iter.hasNext();) {
+			UIComponent col = (UIComponent) iter.next();
+			if (col.isRendered()){
+				String colId = col.getId().toUpperCase();
+				//remove existing item
+				Set<String> toDel = new HashSet<String>();
+				for (String val : widths){
+					if (val.toUpperCase().startsWith(colId+"-")){
+						toDel.add(val);
+					}
+				}//for
+				widths.removeAll(toDel);
+				//create new item
+				String newWidth = newWidths[index++];
+				String item = colId + "-" + newWidth;;
+				widths.add(item);
+			}//if
+		}//for
+		//build new value
+		StringBuilder builder = new StringBuilder();
+		for (String val : widths){
+			builder.append(val).append(SEP);
+		}
+		value = builder.toString();
+	}//changeColumnSize
+	
+}//ColumnsSizeState
+
+class ColumnsOrder implements Serializable{
+
+	private static final long serialVersionUID = 907700564445889954L;
+
+	private static final String SEP = ";";
+
+	private String value;
+
+	private ColumnsOrder() {
+		super();
+	}
+	
+	static ColumnsOrder getColumnsOrder(UIExtendedDataTable extendedDataTable, String val){
+		ColumnsOrder columnsOrder = new ColumnsOrder();
+		columnsOrder.init(extendedDataTable, val);
+		return columnsOrder;
+	}
+	
+	/**
+	 * Converts its state from String representation or create default state if it is not set.
+	 */
+	private void init(UIExtendedDataTable extendedDataTable, String val){
+		value = val;
+		if ((value == null) || (value.length() == 0))
+			createDefaultColumnsOrder(extendedDataTable);
+	}
+	
+	/**
+	 * Converts its state to String representation.
+	 */
+	public String toString(){
+		return value;
+	}
+	
+	/**
+	 * Create default column order based on component children.
+	 */
+	private void createDefaultColumnsOrder(UIExtendedDataTable extendedDataTable){
+		StringBuilder builder = new StringBuilder();
+		for (Iterator<UIColumn> iter = extendedDataTable.getChildColumns(); iter.hasNext();) {
+			UIColumn child = iter.next();
+			builder.append(child.getId().toUpperCase()).append(SEP);
+		}
+		value = builder.toString();
+	}
+	
+	/**
+	 * Get column index in order. 
+	 * @param columnId column id to be found
+	 * @return column index or null if not found 
+	 */
+	private Integer getColumnIndex(String columnId){
+		if (value == null)
+			return null;
+		List<String> list = Arrays.asList(value.toUpperCase().split(SEP));
+		if (list.contains(columnId.toUpperCase()))
+			return list.indexOf(columnId.toUpperCase());
+		return null;
+	}//getColumnIndex
+	
+	/**
+	 * Changes column order. Moves source column to be next to target column.
+	 * @param sourceColumnId source column id to be moved
+	 * @param targetColumnId target column id
+	 * @param dropBefore
+	 */
+	void changeOrder(String sourceColumnId, String targetColumnId, boolean dropBefore){
+		if (value == null)
+			return;
+		if (sourceColumnId.equals(targetColumnId))
+			return;
+		List<String> list = new ArrayList<String>(Arrays.asList(value.toUpperCase().split(SEP)));
+		//get index of source column
+		int sourceIndex = list.indexOf(sourceColumnId.toUpperCase());
+		//remove from order if exist
+		if (sourceIndex != -1)
+			list.remove(sourceIndex);
+		//get index of target column
+		int targetIndex = list.indexOf(targetColumnId.toUpperCase());
+		//add source column after or before target column
+		if (targetIndex == -1)//add to end
+			list.add(sourceColumnId.toUpperCase());
+		else{
+			//add at proper position
+			list.add((targetIndex + (dropBefore ? 0 : 1)), sourceColumnId.toUpperCase());
+		}
+		//convert from List to String
+		StringBuilder builder = new StringBuilder();
+		for (String str : list)
+			builder.append(str).append(SEP);			
+		value = builder.toString();
+	}
+	
+	/**
+	 * Sort column by given order.
+	 * @param context faces context
+	 * @param children list of unsorted columns
+	 * @return list of sorted columns
+	 */
+	List<UIComponent> sortColumns(final FacesContext context, List<UIComponent> children){
+		List<UIComponent> childs = new ArrayList<UIComponent>(children);
+		Collections.sort(childs, new Comparator<UIComponent>() {
+			public int compare(UIComponent o1, UIComponent o2) {
+				Integer index1 = getColumnIndex(o1.getId());
+				Integer index2 = getColumnIndex(o2.getId());
+				if (index1 == null) {
+					return ((index2 == null) ? 0 : 1);
+				}
+				return ((index2 == null) ? -1 : index1.compareTo(index2));
+			}
+		});
+		return childs;
+	}
+
+}//ColumnsOrder
+
+class ColumnsVisibility implements Serializable{
+	
+	private static final long serialVersionUID = -3923409650272094713L;
+
+	private static final String SEP = ";";
+
+	private String value;
+
+	private ColumnsVisibility() {
+		super();
+	}
+	
+	static ColumnsVisibility getColumnsVisibility(UIExtendedDataTable extendedDataTable, String val){
+		ColumnsVisibility columnsVisibility = new ColumnsVisibility();
+		columnsVisibility.init(extendedDataTable, val);
+		return columnsVisibility;
+	}
+	
+	/**
+	 * Converts its state from String representation or create default state if it is not set.
+	 */
+	private void init(UIExtendedDataTable extendedDataTable, String val){
+		value = val;
+		if ((value == null) || (value.length() == 0))
+			createDefaultColumnsVisibility(extendedDataTable);
+		//set visibility flag for all columns
+		for (Iterator<UIColumn> iter = extendedDataTable.getChildColumns(); iter.hasNext();) {
+			UIColumn child = iter.next();
+			if (child instanceof UIColumn) {
+				UIColumn dataColumn = (UIColumn) child;
+				dataColumn.setVisible(isVisible(dataColumn.getId()));
+			}//if
+		}//for
+	}//init
+	
+	/**
+	 * Converts its state to String representation.
+	 */
+	public String toString(){
+		return value;
+	}
+	
+	/**
+	 * Create default column visibility based on component children.
+	 */
+	private void createDefaultColumnsVisibility(UIExtendedDataTable extendedDataTable){
+		StringBuilder builder = new StringBuilder();
+		for (Iterator<UIColumn> iter = extendedDataTable.getChildColumns(); iter.hasNext();) {
+			UIColumn kid = iter.next();
+			builder.append(kid.getId().toUpperCase()).append(SEP);
+		}
+		value = builder.toString();
+	}//createDefaultColumnsVisibility
+	
+	/**
+	 * Get column visibility. 
+	 * @param columnId column id to be found
+	 * @return true if column is visible, otherwise false
+	 */
+	boolean isVisible(String columnId){
+		if (value == null)
+			return true;
+		Set<String> visibleIds = new HashSet<String>(Arrays.asList(value.toUpperCase().split(SEP)));
+		return visibleIds.contains(columnId.toUpperCase());
+	}//isVisible
+	
+	/**
+	 * Toggle column visibility.
+	 * @param extendedDataTable table component
+	 * @param columnId column id
+	 */
+	void toggleVisibility(UIExtendedDataTable extendedDataTable, String columnId){
+		if (value == null)
+			return;
+		UIColumn column = null;
+		//find column by id
+		for (Iterator<UIColumn> iter = extendedDataTable.getChildColumns(); iter.hasNext();) {
+			UIColumn col = iter.next();
+			if (col.getId().equalsIgnoreCase(columnId)){
+				if (col instanceof UIColumn){
+					column = (UIColumn) col;
+				}
+				break;
+			}//if
+		}//for
+		if (column == null)
+			return;
+		boolean visible = column.isVisible();
+		//toggle visibility
+		visible = !visible;
+		//set visibility flag for column
+		column.setVisible(visible);
+		Set<String> visibleIds = new HashSet<String>(Arrays.asList(value.toUpperCase().split(SEP)));
+		if (visible){
+			//add id to set
+			visibleIds.add(columnId.toUpperCase());
+		}
+		else{
+			//remove id from list
+			visibleIds.remove(columnId.toUpperCase());
+		}
+		//convert from Set to String
+		StringBuilder builder = new StringBuilder();
+		for (String str : visibleIds)
+			builder.append(str).append(SEP);			
+		value = builder.toString();
+	}//changeVisibility
+
+}//ColumnsVisibility
+
+class ColumnGroupingState implements Serializable{
+	
+	private static final long serialVersionUID = -3923409650272094713L;
+
+	private static final String SEP = ";";
+	//private static final String TRUE = "1";
+	//private static final String FALSE = "0";
+	private static final Boolean DEF = Boolean.TRUE;//expanded
+
+	private String columnId;
+	private List<Boolean> groupExpanded;
+	private Ordering ordering; 
+	//private String value;
+
+	private ColumnGroupingState() {
+		super();
+	}
+	
+	static ColumnGroupingState getColumnGropingState(UIExtendedDataTable extendedDataTable, String val){
+		ColumnGroupingState groupingState = new ColumnGroupingState();
+		groupingState.init(extendedDataTable, val);
+		return groupingState;
+	}
+	
+	/**
+	 * Converts its state from String representation or create default state if it is not set.
+	 */
+	private void init(UIExtendedDataTable extendedDataTable, String val){
+		columnId = null;
+		ordering = Ordering.UNSORTED;
+		groupExpanded = new ArrayList<Boolean>();
+		if ((val == null) || (val.length() == 0))
+			return;
+		List<String> tmp = Arrays.asList(val.split(SEP));
+		if (!tmp.isEmpty()){
+			columnId = tmp.get(0);						//column id
+			ordering = Ordering.valueOf(tmp.get(1));	//sort order
+			if (ordering == null)
+				ordering = Ordering.UNSORTED;
+//			tmp = tmp.subList(2, tmp.size());//remove fist and second item
+//			for (String s : tmp){
+//				groupExpanded.add(Boolean.valueOf(s.equals(TRUE)));
+//			}//for
+		}
+		//get column by id and set sort order
+		for (Iterator<UIColumn> columns = extendedDataTable.getChildColumns(); columns.hasNext(); ){
+			UIColumn child = columns.next();
+			if (columnId.equalsIgnoreCase(child.getId())) {
+				child.setSortOrder(ordering);
+				break;
+			}
+		}
+	}//init
+	
+	/**
+	 * Converts its state to String representation.
+	 */
+	public String toString(){
+		if (columnId == null)
+			return "";
+		StringBuilder builder = new StringBuilder();
+		builder.append(columnId).append(SEP);	//add column id
+		builder.append(ordering).append(SEP);	//add sort order
+//		for (Boolean b : groupExpanded){
+//			builder.append(b ? TRUE : FALSE).append(SEP);
+//		}
+		return builder.toString();
+	}
+	
+	/**
+	 * Gets grouped column id.
+	 * @return grouped column id if grouping is on, otherwise false
+	 */
+	String getGroupingColumnId(){
+		return columnId;
+	}
+	
+	/**
+	 * Checks if grouping is on. 
+	 * @return true if grouping is on, otherwise false
+	 */
+	boolean isGroupingOn(){
+		return (columnId != null);
+	}
+	
+	/**
+	 * Turn on grouping for column. 
+	 * @param colId id of column to be grouped
+	 * @param ordering sort order
+	 */
+	void groupBy(String colId, Ordering ordering){
+		columnId = colId;
+		this.ordering = ordering;
+		resetGroupVisibilityState();
+	}
+	
+	/**
+	 * Resets information about group visibility state.
+	 * All group will be mark as expanded.
+	 */
+	void resetGroupVisibilityState(){
+		groupExpanded.clear();
+	}
+	
+	/**
+	 * Turn off grouping. 
+	 */
+	void disableGrouping(){
+		columnId = null;
+		ordering = Ordering.UNSORTED;
+		resetGroupVisibilityState();
+	}
+	
+	/**
+	 * Toggle group. It means that group will be expanded if is collapsed
+	 * and group will be collapsed if is expanded.
+	 * @param index index of group to be toggled 
+	 */
+	void toggleGroup(int index){
+		if (index < 0)
+			throw new IllegalArgumentException("Illegal index value :"+index);
+		if (index >= groupExpanded.size()){
+			//add default values for lower indexes
+			int count = index - groupExpanded.size() + 1;
+			for (int i = 0; i < count; i++){
+				groupExpanded.add(DEF);
+			}///for
+		}
+		groupExpanded.add(index,!groupExpanded.remove(index));
+	}
+	
+	/**
+	 * Checks if group is expanded. 
+	 * @param index index of group to be tested 
+	 * @return true if group is expanded, otherwise false
+	 */
+	boolean groupIsExpanded(int index){
+		if (index < 0)
+			throw new IllegalArgumentException("Illegal index value :"+index);
+		if (index >= groupExpanded.size()){
+			return DEF;
+		}
+		return groupExpanded.get(index).booleanValue();
+	}
+
+}//ColumnGroupingState
\ No newline at end of file

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/AbstractExtendedRowsRenderer.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/AbstractExtendedRowsRenderer.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/AbstractExtendedRowsRenderer.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,393 @@
+/**
+ * License Agreement.
+ *
+ *  JBoss RichFaces - Ajax4jsf Component Library
+ *
+ * Copyright (C) 2007  Exadel, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+package org.richfaces.renderkit;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+
+import org.ajax4jsf.component.SequenceDataAdaptor;
+import org.ajax4jsf.component.UIDataAdaptor;
+import org.ajax4jsf.model.DataVisitor;
+import org.ajax4jsf.renderkit.HeaderResourcesRendererBase;
+import org.ajax4jsf.renderkit.RendererUtils;
+import org.ajax4jsf.renderkit.RendererUtils.HTML;
+import org.ajax4jsf.resource.InternetResource;
+import org.richfaces.component.Row;
+import org.richfaces.renderkit.AbstractRowsRenderer;
+import org.richfaces.renderkit.CompositeRenderer;
+import org.richfaces.renderkit.RendererContributor;
+import org.richfaces.renderkit.ScriptOptions;
+import org.richfaces.renderkit.TableHolder;
+
+import org.richfaces.component.UIExtendedDataTable;
+import org.richfaces.renderkit.html.TableSelectionRendererContributor;
+
+/**
+ * @author shura
+ * 
+ */
+public abstract class AbstractExtendedRowsRenderer extends
+        HeaderResourcesRendererBase implements DataVisitor {
+
+    protected static final String MENU_ID = "_TABLE_MENU_ID_";
+    protected boolean firstRow = true;
+
+    protected class CompositeRendererEnabler extends CompositeRenderer {
+        public CompositeRendererEnabler() {
+            addContributor(new TableSelectionRendererContributor());
+        }
+
+        protected Class<? extends UIComponent> getComponentClass() {
+            return AbstractExtendedRowsRenderer.this.getComponentClass();
+        }
+
+        public void mergeScriptOptions(ScriptOptions scriptOptions,
+                FacesContext context, UIComponent component) {
+            super.mergeScriptOptions(scriptOptions, context, component);
+
+        }
+
+        public String getScriptContributions(String varString,
+                FacesContext context, UIComponent component) {
+            return super.getScriptContributions(varString, context, component);
+        }
+
+        public RendererContributor[] getContributors() {
+            return super.getContributors();
+        }
+
+        public InternetResource[] getScripts() {
+            return super.getScripts();
+        }
+
+        public InternetResource[] getStyles() {
+            return super.getStyles();
+        }
+    }
+
+    public static final String[][] TABLE_EVENT_ATTRS = {
+            { "onclick", "onRowClick" }, { "ondblclick", "onRowDblClick" },
+            { "onmousemove", "onRowMouseMove" },
+            { "onmouseup", "onRowMouseUp" },
+            { "onmousedown", "onRowMouseDown" },
+            { "onmouseover", "onRowMouseOver" },
+            { "onmouseout", "onRowMouseOut" } };
+
+    public static final String ROW_CLASS_KEY = AbstractRowsRenderer.class
+            .getName()
+            + ".rowClass";
+
+    public static final String SKIN_ROW_CLASS_KEY = AbstractRowsRenderer.class
+            .getName()
+            + ".skinRowClass";
+
+    public static final String CELL_CLASS_KEY = AbstractRowsRenderer.class
+            .getName()
+            + ".cellClass";
+
+    public static final String SKIN_CELL_CLASS_KEY = AbstractRowsRenderer.class
+            .getName()
+            + ".skinCellClass";
+
+    public static final String SKIN_FIRST_ROW_CLASS_KEY = AbstractRowsRenderer.class
+            .getName()
+            + ".firstRowSkinClass";
+
+    // protected TableMenuRenderer menuRenderer = new RichTableMenuRenderer();
+
+    protected CompositeRendererEnabler composite = new CompositeRendererEnabler();
+
+    // protected Object lastData = null;
+    // protected Object lastKey = null;
+    // TODO move this property to holder
+    // protected int groupIndex = -1;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.ajax4jsf.ajax.repeat.DataVisitor#process(javax.faces.context.FacesContext,
+     *      java.lang.Object, java.lang.Object)
+     */
+    public void process(FacesContext context, Object rowKey, Object argument)
+            throws IOException {
+        ExtendedTableHolder holder = (ExtendedTableHolder) argument;
+        UIDataAdaptor table = holder.getTable();
+        if (holder.getLastKey() != null) {
+            table.setRowKey(context, holder.getLastKey());
+            holder.setLastData(table.getRowData());
+        }
+        holder.setLastKey(rowKey);
+        table.setRowKey(context, rowKey);
+        encodeOneRow(context, holder);
+        holder.nextRow();
+    }
+
+    public void encodeRows(FacesContext context, UIComponent component)
+            throws IOException {
+        encodeRows(context, component, new ExtendedTableHolder(
+                (UIExtendedDataTable) component));
+    }
+
+    /**
+     * Iterate over all rows for this table.
+     * 
+     * @param context
+     * @param component
+     * @throws IOException
+     */
+    protected void encodeRows(FacesContext context, UIComponent component,
+            ExtendedTableHolder tableHolder) throws IOException {
+        this.firstRow = true;
+        UIDataAdaptor table = (UIDataAdaptor) component;
+        Object key = table.getRowKey();
+        table.captureOrigValue(context);
+        table.walk(context, this, tableHolder);
+        doCleanup(context, tableHolder);
+        table.setRowKey(key);
+        table.restoreOrigValue(context);
+    }
+
+    /**
+     * @param context
+     *            TODO
+     * @param tableHolder
+     * @throws IOException
+     */
+    protected void doCleanup(FacesContext context, TableHolder tableHolder)
+            throws IOException {
+        // Hoock method for perform encoding after all rows is rendered
+
+    }
+
+    public abstract void encodeOneRow(FacesContext context,
+            ExtendedTableHolder holder) throws IOException;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see javax.faces.render.Renderer#getRendersChildren()
+     */
+    public boolean getRendersChildren() {
+        return true;
+    }
+
+    public void encodeChildren(FacesContext context, UIComponent component)
+            throws IOException {
+        encodeRows(context, component);
+    }
+
+    public void encodeCaption(FacesContext context, SequenceDataAdaptor table)
+            throws IOException {
+        ResponseWriter writer = context.getResponseWriter();
+        UIComponent caption = table.getFacet("caption");
+        if (caption != null) {
+            String captionClass = (String) table.getAttributes().get(
+                    "captionClass");
+            String captionStyle = (String) table.getAttributes().get(
+                    "captionStyle");
+            writer.startElement("caption", table);
+            if (captionClass != null) {
+                captionClass = "dr-table-caption rich-table-caption "
+                        + captionClass;
+            } else {
+                captionClass = "dr-table-caption rich-table-caption";
+            }
+            writer.writeAttribute("class", captionClass, "captionClass");
+            if (captionStyle != null) {
+                writer.writeAttribute("style", captionStyle, "captionStyle");
+            }
+            renderChild(context, caption);
+            writer.endElement("caption");
+        }
+
+    }
+
+    /**
+     * @param context
+     * @param table
+     * @throws IOException
+     */
+    protected void encodeRowEvents(FacesContext context, UIDataAdaptor table)
+            throws IOException {
+        RendererUtils utils2 = getUtils();
+        for (int i = 0; i < TABLE_EVENT_ATTRS.length; i++) {
+            String[] attrs = TABLE_EVENT_ATTRS[i];
+            utils2.encodeAttribute(context, table, attrs[1], attrs[0]);
+        }
+    }
+
+    /**
+     * Encode HTML "class" attribute, if is not empty. Classes combined from
+     * pre-defined skin classes, class from parent component, and custom
+     * attribute.
+     * 
+     * @param writer
+     * @param parentPredefined
+     *            TODO
+     * @param predefined
+     *            predefined skin classes
+     * @param parent
+     *            class from parent component
+     * @param custom
+     *            custom classes.
+     * @throws IOException
+     */
+    protected void encodeStyleClass(ResponseWriter writer,
+            Object parentPredefined, Object predefined, Object parent,
+            Object custom) throws IOException {
+        StringBuffer styleClass = new StringBuffer();
+        // Construct predefined classes
+        if (null != parentPredefined) {
+            styleClass.append(parentPredefined).append(" ");
+        } else if (null != predefined) {
+            styleClass.append(predefined).append(" ");
+        }
+        // Append class from parent component.
+        if (null != parent) {
+            styleClass.append(parent).append(" ");
+        }
+        if (null != custom) {
+            styleClass.append(custom);
+        }
+        if (styleClass.length() > 0) {
+            writer.writeAttribute(HTML.class_ATTRIBUTE, styleClass,
+                    "styleClass");
+        }
+    }
+
+    protected void encodeStyle(ResponseWriter writer, Object parentPredefined,
+            Object predefined, Object parent, Object custom) throws IOException {
+        StringBuffer style = new StringBuffer();
+        // Construct predefined styles
+        if (null != parentPredefined) {
+            style.append(parentPredefined).append(" ");
+        } else if (null != predefined) {
+            style.append(predefined).append(" ");
+        }
+        // Append style from parent component.
+        if (null != parent) {
+            style.append(parent).append(" ");
+        }
+        if (null != custom) {
+            style.append(custom);
+        }
+        if (style.length() > 0) {
+            writer.writeAttribute("style", style, "style");
+        }
+    }
+
+    /**
+     * Render component and all its children with current row/cell style
+     * classes.
+     * 
+     * @param context
+     * @param cell
+     * @param skinFirstRowClass
+     *            TODO
+     * @param skinRowClass
+     *            TODO
+     * @param rowClass
+     * @param skinCellClass
+     *            TODO
+     * @param cellClass
+     * @throws IOException
+     */
+    protected void encodeCellChildren(FacesContext context, UIComponent cell,
+            String skinFirstRowClass, String skinRowClass, String rowClass,
+            String skinCellClass, String cellClass) throws IOException {
+        Map<String, Object> requestMap = context.getExternalContext()
+                .getRequestMap();
+        // Save top level class parameters ( if any ), and put new for this
+        // component
+        Object savedRowClass = requestMap.get(ROW_CLASS_KEY);
+        if (null != rowClass) {
+            requestMap.put(ROW_CLASS_KEY, rowClass);
+
+        }
+        Object savedSkinFirstRowClass = requestMap
+                .get(SKIN_FIRST_ROW_CLASS_KEY);
+        if (null != skinRowClass) {
+            requestMap.put(SKIN_FIRST_ROW_CLASS_KEY, skinFirstRowClass);
+
+        }
+        Object savedSkinRowClass = requestMap.get(SKIN_ROW_CLASS_KEY);
+        if (null != skinRowClass) {
+            requestMap.put(SKIN_ROW_CLASS_KEY, skinRowClass);
+
+        }
+        Object savedCellClass = requestMap.get(CELL_CLASS_KEY);
+        if (null != cellClass) {
+            requestMap.put(CELL_CLASS_KEY, cellClass);
+        }
+        Object savedSkinCellClass = requestMap.get(SKIN_CELL_CLASS_KEY);
+        if (null != skinCellClass) {
+            requestMap.put(SKIN_CELL_CLASS_KEY, skinCellClass);
+
+        }
+        renderChild(context, cell);
+        // Restore original values.
+        requestMap.put(ROW_CLASS_KEY, savedRowClass);
+        requestMap.put(CELL_CLASS_KEY, savedCellClass);
+        requestMap.put(SKIN_FIRST_ROW_CLASS_KEY, savedSkinFirstRowClass);
+        requestMap.put(SKIN_ROW_CLASS_KEY, savedSkinRowClass);
+        requestMap.put(SKIN_CELL_CLASS_KEY, savedSkinCellClass);
+
+    }
+
+    protected void encodeTableHeaderFacet(FacesContext context, int columns,
+            ResponseWriter writer, UIComponent footer,
+            String skinFirstRowClass, String skinRowClass,
+            String skinCellClass, String footerClass, String element,
+            String facetName) throws IOException {
+        boolean isColgroup = footer instanceof Row;
+        if (!isColgroup) {
+            writer.startElement("tr", footer);
+            encodeStyleClass(writer, null, skinFirstRowClass, footerClass, null);
+            writer.startElement(element, footer);
+            encodeStyleClass(writer, null, skinCellClass, footerClass, null);
+            if (columns > 0) {
+                writer.writeAttribute("colspan", String.valueOf(columns), null);
+            }
+            writer.writeAttribute("scope", "colgroup", null);
+        }
+        encodeCellChildren(context, footer, skinFirstRowClass, skinRowClass,
+                footerClass, skinCellClass, null);
+        if (!isColgroup) {
+            writer.endElement(element);
+            writer.endElement("tr");
+        }
+
+    }
+
+    protected InternetResource[] getScripts() {
+        return composite.getScripts();
+    }
+
+    protected InternetResource[] getStyles() {
+        return composite.getStyles();
+    }
+
+}

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/AbstractExtendedTableRenderer.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/AbstractExtendedTableRenderer.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/AbstractExtendedTableRenderer.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,1721 @@
+/**
+ * License Agreement.
+ *
+ *  JBoss RichFaces - Ajax4jsf Component Library
+ *
+ * Copyright (C) 2007  Exadel, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+package org.richfaces.renderkit;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIInput;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+
+import org.ajax4jsf.context.AjaxContext;
+import org.ajax4jsf.javascript.JSFunction;
+import org.ajax4jsf.javascript.JSFunctionDefinition;
+import org.ajax4jsf.javascript.JSReference;
+import org.ajax4jsf.javascript.ScriptUtils;
+import org.ajax4jsf.renderkit.AjaxRendererUtils;
+import org.ajax4jsf.renderkit.RendererUtils.HTML;
+import org.richfaces.component.Column;
+import org.richfaces.component.Row;
+import org.richfaces.component.UIColumn;
+import org.richfaces.component.UIDataTable;
+import org.richfaces.component.UIExtendedDataTable;
+import org.richfaces.component.nsutils.NSUtils;
+import org.richfaces.event.ChangeColumnVisibilityEvent;
+import org.richfaces.event.ColumnResizeEvent;
+import org.richfaces.event.DragDropEvent;
+import org.richfaces.model.Ordering;
+import org.richfaces.model.SortField2;
+import org.richfaces.model.impl.expressive.JavaBeanWrapper;
+import org.richfaces.model.impl.expressive.ObjectWrapperFactory;
+import org.richfaces.model.impl.expressive.WrappedBeanComparator2;
+import org.richfaces.renderkit.html.HTMLEncodingContributor;
+import org.richfaces.renderkit.html.RichTableMenuRenderer;
+import org.richfaces.renderkit.html.TableDragDropRenderer;
+import org.richfaces.renderkit.html.TableMenuRenderer;
+import org.richfaces.renderkit.html.iconimages.DataTableIconSortNone;
+import org.richfaces.renderkit.html.images.TriangleIconDown;
+import org.richfaces.renderkit.html.images.TriangleIconUp;
+
+/**
+ * @author shura
+ * 
+ */
+public abstract class AbstractExtendedTableRenderer extends
+        AbstractExtendedRowsRenderer {
+
+    private static final String SORT_FILTER_PARAMETER = "fsp";
+
+    private static final String SORT_DIR_PARAMETER = "sortDir";
+
+    private static final String SORT_DIR_PARAMETER_ASC = "asc";
+
+    private static final String SORT_DIR_PARAMETER_DESC = "desc";
+
+    private static final String FILTER_INPUT_FACET_NAME = "filterValueInput";
+
+    private static final String COL_RESIZE_ACTION_NAME = "columnResizeAction";
+
+    private final static String CHANGE_COL_VISIBILITY = "change_col_v";
+
+    private static final String SORT_FUNCTION = "sortFunction";
+
+    private static final String SHOW_MENU_FUNCTION = "showMenuFunction";
+
+    private static final String ON_RESIZE_FUNCTION = "onColumnResize";
+
+    private static final String GROUP_FILTER_PARAMETER = "groupParam";
+
+    private static final String ON_GROUP_TOGGLE_FUNCTION = "onGroupToggleFunction";
+
+    private static final String GROUP_TOGGLE_ACTION_NAME = "groupToggleAction";
+
+    /**
+     * Encode all table structure - colgroups definitions, caption, header,
+     * footer
+     * 
+     * @param context
+     * @param table
+     * @throws IOException
+     */
+
+    public void encodeTableStructure(FacesContext context,
+            UIExtendedDataTable table) throws IOException {
+        ResponseWriter writer = context.getResponseWriter();
+        // int columns = getColumnsCount(table);
+        Iterator<UIColumn> cols = table.getSortedColumns();
+        // columns = extendedDataTable.getVisibleColumnsCount();
+        // Encode colgroup definition.
+        // writer.startElement("colgroup", table);
+        // writer.writeAttribute("span", String.valueOf(columns), null);
+
+        while (cols.hasNext()) {
+            UIColumn col = cols.next();
+            if (col.isRendered()) {
+                writer.startElement("col", table);
+                writer.writeAttribute("width", table.getColumnSize(col), null);
+                writer.endElement("col");
+            }
+        }
+        writer.startElement("col", table);
+        writer.writeAttribute("width", "1*", null);
+        writer.endElement("col");
+        // writer.endElement("colgroup");
+        encodeCaption(context, table);
+    }
+
+    public void encodeHeader(FacesContext context, UIExtendedDataTable table)
+            throws IOException {
+
+        ResponseWriter writer = context.getResponseWriter();
+        UIComponent header = table.getHeader();
+        boolean columnFacetPresent = isColumnFacetPresent(table, "header");
+        Iterator<UIColumn> colums = table.getSortedColumns();
+        // int numberOfColumns = getColumnsCount(table);
+        int numberOfColumns = table.getVisibleColumnsCount() + 1;
+        if (header != null || columnFacetPresent) {
+            writer.startElement("thead", table);
+            writer.writeAttribute(HTML.id_ATTRIBUTE, table
+                    .getBaseClientId(context)
+                    + ":header", null);
+            writer.writeAttribute(HTML.class_ATTRIBUTE, "dr-table-thead", null);
+            String headerClass = (String) table.getAttributes().get(
+                    "headerClass");
+            // String menuId = (String) table.getAttributes().get(MENU_ID);
+            if (header != null) {
+                writer.startElement("tr", header);
+                encodeStyleClass(writer, null,
+                        "dr-table-header rich-table-header", headerClass, null);
+                writer.writeAttribute(HTML.id_ATTRIBUTE, table
+                        .getBaseClientId(context)
+                        + ":fakeIeRow", null);
+                for (int i = 0; i < numberOfColumns; i++) {
+                    writer.startElement("th", header);
+                    encodeStyleClass(writer, null,
+                            "dr-table-headercell rich-table-headercell",
+                            headerClass, null);
+                    writer.endElement("th");
+                }
+                writer.endElement("tr");
+
+                encodeTableHeaderFacet(context, numberOfColumns, writer,
+                        header, "dr-table-header rich-table-header",
+                        "dr-table-header-continue rich-table-header-continue",
+                        "dr-table-headercell rich-table-headercell",
+                        headerClass, "th", "header");
+            }
+
+            if (columnFacetPresent) {
+                writer.startElement("tr", table);
+                writer.writeAttribute(HTML.id_ATTRIBUTE, table
+                        .getBaseClientId(context)
+                        + ":headerRow", null);
+                encodeStyleClass(writer, null,
+                        "dr-table-subheader rich-table-subheader", null,
+                        headerClass);
+                encodeHeaderFacets(
+                        context,
+                        writer,
+                        table,
+                        colums,
+                        "extdt-dr-menucell extdt-subheadercell rich-table-subheadercell",
+                        headerClass, "header", "th", numberOfColumns);
+                /* encoding additional empty column used in resizing columns */
+                writer.startElement("th", table);
+                encodeStyleClass(writer, null,
+                        "extdt-subheadercell rich-table-subheadercell", null,
+                        null);
+                writer.endElement("th");
+                writer.endElement("tr");
+
+                encodeFilterRow(context, writer, table, table
+                        .getSortedColumns(),
+                        "extdt-subheadercell rich-table-subheadercell",
+                        headerClass, "header", HTML.th_ELEM);
+
+            }
+            writer.endElement("thead");
+        }
+    }
+
+    private void encodeFilterRow(FacesContext context, ResponseWriter writer,
+            UIExtendedDataTable table, Iterator<UIColumn> headers,
+            String skinCellClass, String headerClass, String facetName,
+            String element) throws IOException {
+        if (filteringEnabled(table)) {
+            writer.startElement(HTML.TR_ELEMENT, table);
+            writer.writeAttribute(HTML.id_ATTRIBUTE, table
+                    .getBaseClientId(context)
+                    + ":filterRow", null);
+            encodeStyleClass(writer, null,
+                    "extdt-table-filterrow rich-table-subheader", null,
+                    headerClass);
+            encodeFilterInputs(context, writer, table, headers, skinCellClass,
+                    headerClass, facetName, element);
+            writer.startElement(HTML.th_ELEM, table);
+            encodeStyleClass(writer, null,
+                    "extdt-subheadercell rich-table-subheadercell", null, null);
+            writer.endElement(HTML.th_ELEM);
+            writer.endElement(HTML.TR_ELEMENT);
+        }
+    }
+
+    /**
+     * Checks if column has filtering enabled
+     * 
+     * @param dataColumn
+     *            column to inspect
+     * @return true if filtering is enabled, false otherwise
+     * @author pbuda
+     */
+    private boolean filterEnabledColumn(UIColumn dataColumn) {
+        return dataColumn.getFilterMethod() == null
+                && dataColumn.getValueExpression("filterExpression") == null
+                && dataColumn.getValueExpression("filterBy") != null;
+    }
+
+    /**
+     * Checks whether filtering is enabled in this table
+     * 
+     * @param table
+     *            table to inspect
+     * @return true if filtering is enabled, false otherwise
+     * @author pbuda
+     */
+    private boolean filteringEnabled(UIExtendedDataTable table) {
+        Iterator<UIColumn> columns = table.getSortedColumns();
+        boolean enabled = false;
+        while (columns.hasNext()) {
+            UIColumn col = columns.next();
+            if (col instanceof UIColumn) {
+                UIColumn dataColumn = (UIColumn) col;
+                if (filterEnabledColumn(dataColumn) && dataColumn.isRendered()) {
+                    enabled = true;
+                    break;
+                }
+            }
+        }
+        return enabled;
+    }
+
+    /**
+     * Encodes a new row of the table and places filter inputs for corresponding
+     * columns in that new row
+     * 
+     * @param context
+     *            current FacesContext instance
+     * @param writer
+     *            ResponseWriter for this context
+     * @param table
+     *            table instance
+     * @param headers
+     *            iterator over headers in table
+     * @param skinCellClass
+     *            css class of skin
+     * @param headerClass
+     *            css class of header
+     * @param facetName
+     *            facet to encode
+     * @param element
+     *            element to encode
+     * @throws IOException
+     *             if ResponseWriter fails it's operation
+     * @author pbuda
+     */
+    private void encodeFilterInputs(FacesContext context,
+            ResponseWriter writer, UIDataTable table,
+            Iterator<UIColumn> headers, String skinCellClass,
+            String headerClass, String facetName, String element)
+            throws IOException {
+        while (headers.hasNext()) {
+            UIColumn column = headers.next();
+            if (column instanceof UIColumn) {
+                if (column.isRendered()) {
+                    writer.startElement(element, table);
+                    String classAttribute = facetName + "Class";
+                    String columnHeaderClass = (String) column.getAttributes()
+                            .get(classAttribute);
+                    encodeStyleClass(writer, null, skinCellClass, headerClass,
+                            columnHeaderClass);
+                    UIColumn dataColumn = (UIColumn) column;
+                    if (filterEnabledColumn(dataColumn)) {
+                        writer.startElement(HTML.DIV_ELEM, column);
+                        addInplaceInput(context, column, buildAjaxFunction(
+                                context, column, false,
+                                getOnAjaxCompleteFunction(context,
+                                        (UIDataTable) column.getParent())));
+                        writer.endElement(HTML.DIV_ELEM);
+                    }
+                    writer.endElement(element);
+                }
+            }
+        }
+    }
+
+    public boolean isColumnFacetPresent(UIDataTable table, String facetName) {
+        Iterator<UIComponent> columns = table.columns();
+        boolean result = false;
+        while (columns.hasNext() && !result) {
+            UIComponent component = columns.next();
+            if (isColumnRendered(component)) {
+                if (null != component.getFacet(facetName)) {
+                    result = true;
+                } /*
+                     * else if(component instanceof Column) { Column column =
+                     * (Column)component; result = column.isSelfSorted(); }
+                     */
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @param component
+     * @return
+     */
+    protected boolean isColumnRendered(UIComponent component) {
+        boolean rendered = true;
+        try {
+            rendered = component.isRendered();
+        } catch (Exception e) {
+            // DO nothing, rendered binded to row variable;
+        }
+        return rendered;
+    }
+
+    protected void encodeHeaderFacets(FacesContext context,
+            ResponseWriter writer, UIDataTable table,
+            Iterator<UIColumn> headers, String skinCellClass,
+            String headerClass, String facetName, String element, int colCount)
+            throws IOException {
+        int t_colCount = 0;
+
+        // HeaderEncodeStrategy richEncodeStrategy = new
+        // RichHeaderEncodeStrategy();
+        // HeaderEncodeStrategy simpleEncodeStrategy = new
+        // SimpleHeaderEncodeStrategy();
+
+        while (headers.hasNext()) {
+            UIColumn column = headers.next();
+            if (isColumnRendered(column)) {
+                if ((Integer) column.getAttributes().get("colspan") != null) {
+                    t_colCount = t_colCount
+                            + ((Integer) column.getAttributes().get("colspan"))
+                                    .intValue();
+                } else {
+                    t_colCount++;
+                }
+                if (t_colCount > colCount) {
+                    break;
+                }
+
+                String classAttribute = facetName + "Class";
+                String columnHeaderClass = (String) column.getAttributes().get(
+                        classAttribute);
+
+                writer.startElement(element, column);
+                encodeStyleClass(writer, null, skinCellClass, headerClass,
+                        columnHeaderClass);
+                writer.writeAttribute("scope", "col", null);
+                getUtils().encodeAttribute(context, column, "colspan");
+
+                boolean sortableColumn = column
+                        .getValueExpression("comparator") != null
+                        || column.getValueExpression("sortBy") != null;
+                column.getAttributes().put("sortable",
+                        Boolean.valueOf(sortableColumn));
+
+                HeaderEncodeStrategy strategy = (column instanceof UIColumn && "header"
+                        .equals(facetName)) ? new G3HeaderEncodeStrategy()
+                        : new RichHeaderEncodeStrategy();
+
+                strategy.encodeBegin(context, writer, column, facetName,
+                        sortableColumn);
+
+                UIComponent facet = column.getFacet(facetName);
+                if (facet != null && isColumnRendered(facet)) {
+                    renderChild(context, facet);
+                }
+
+                strategy.encodeEnd(context, writer, column, facetName,
+                        sortableColumn);
+
+                writer.endElement(element);
+
+            }
+
+        }
+    }
+
+    public void encodeFooter(FacesContext context, UIExtendedDataTable table)
+            throws IOException {
+
+        ResponseWriter writer = context.getResponseWriter();
+        UIComponent footer = table.getFooter();
+        boolean columnFacetPresent = isColumnFacetPresent(table, "footer");
+        Iterator<UIColumn> tableColumns = table.getSortedColumns();
+        // int columns = getColumnsCount(table);
+        int columns = table.getVisibleColumnsCount() + 1;
+        if (footer != null || columnFacetPresent) {
+            writer.startElement("tfoot", table);
+            String footerClass = (String) table.getAttributes().get(
+                    "footerClass");
+
+            if (columnFacetPresent) {
+                writer.startElement("tr", table);
+                encodeStyleClass(writer, null,
+                        "dr-table-subfooter rich-table-subfooter", null,
+                        footerClass);
+
+                encodeHeaderFacets(context, writer, table, tableColumns,
+                        "dr-table-subfootercell rich-table-subfootercell",
+                        footerClass, "footer", "td", columns);
+
+                writer.endElement("tr");
+            }
+            if (footer != null) {
+                encodeTableHeaderFacet(context, columns, writer, footer,
+                        "dr-table-footer rich-table-footer",
+                        "dr-table-footer-continue rich-table-footer-continue",
+                        "dr-table-footercell rich-table-footercell",
+                        footerClass, "td", "footer");
+            }
+            writer.endElement("tfoot");
+        }
+
+    }
+
+    /**
+     * Encodes the false row to enable proper rendering in IE when rows grouping
+     * is on (IE won't render the table properly if it has first row with
+     * colspan).
+     * 
+     * @param table
+     *            to render false row to,
+     * @param context
+     *            of the table.
+     * @throws IOException
+     */
+
+    private void encodeFakeIeRow(FacesContext context,
+            UIExtendedDataTable table, ExtendedTableHolder holder)
+            throws IOException {
+
+        ResponseWriter writer = context.getResponseWriter();
+        int numberOfColumns = getColumnsCount(table) + 1;
+
+        writer.startElement("tr", table);
+        writer.writeAttribute(HTML.id_ATTRIBUTE, table.getBaseClientId(context)
+                + ":body:fakeIeRow", null);
+        for (int i = 0; i < numberOfColumns; i++) {
+            writer.startElement("td", table);
+            encodeStyleClass(writer, null, getCellSkinClass(), null, null);
+            writer.endElement("td");
+        }
+        writer.endElement("tr");
+
+    }
+
+    /**
+     * Encodes the grouping row.
+     * 
+     * @param table
+     *            to render group row to,
+     * @param context
+     *            of the table.
+     * @throws IOException
+     */
+    private void encodeGroupRow(FacesContext context,
+            UIExtendedDataTable table, ExtendedTableHolder holder)
+            throws IOException {
+
+        ResponseWriter writer = context.getResponseWriter();
+        int numberOfColumns = getColumnsCount(table) + 1;
+        int actGroupRow = holder.getGroupRowCounter();
+        writer.startElement(HTML.TR_ELEMENT, table);
+        writer.writeAttribute(HTML.class_ATTRIBUTE, "extdt-group-row", null);
+        writer.writeAttribute(HTML.id_ATTRIBUTE, table.getBaseClientId(context)
+                + ":group-row:" + actGroupRow, null);
+
+        writer.writeAttribute("expanded",
+                table.groupIsExpanded(actGroupRow) ? "true" : "false", null);
+        writer.writeAttribute("groupindex", String.valueOf(actGroupRow), null);
+
+        writer.startElement(HTML.td_ELEM, table);
+        writer.writeAttribute(HTML.class_ATTRIBUTE,
+                "dr-table-cell extdt-group-cell", null);
+        writer.writeAttribute("colspan", numberOfColumns, null);
+        writer.startElement(HTML.SPAN_ELEM, table);
+        writer.startElement(HTML.IMG_ELEMENT, table);
+
+        String imagePlusUri = getResource(
+                "/org/richfaces/renderkit/html/images/plusIcon.gif").getUri(
+                context, null);
+        String imageMinusUri = getResource(
+                "/org/richfaces/renderkit/html/images/minusIcon.gif").getUri(
+                context, null);
+        if (table.groupIsExpanded(actGroupRow)) {
+            writer.writeAttribute(HTML.src_ATTRIBUTE, imageMinusUri, null);
+            writer.writeAttribute("alternatesrc", imagePlusUri, null);
+        } else {
+            writer.writeAttribute(HTML.src_ATTRIBUTE, imagePlusUri, null);
+            writer.writeAttribute("alternatesrc", imageMinusUri, null);
+        }
+        writer.endElement(HTML.IMG_ELEMENT);
+        writer.endElement(HTML.SPAN_ELEM);
+        writer.startElement(HTML.SPAN_ELEM, table);
+        writer.writeAttribute(HTML.class_ATTRIBUTE, "extdt-group-text", null);
+        writer.writeAttribute(HTML.style_ATTRIBUTE, "font-weight: bold;", null);
+        String label = holder.getGroupingColumnLabel();
+        writer.writeText((label == null) ? "" : label + ": ", null);
+        writer.endElement(HTML.SPAN_ELEM);
+        renderChildren(context, holder.getGroupingColumn());
+        writer.startElement(HTML.SPAN_ELEM, table);
+        writer.writeAttribute(HTML.class_ATTRIBUTE, "extdt-group-text", null);
+        writer.endElement(HTML.SPAN_ELEM);
+        writer.endElement(HTML.td_ELEM);
+        writer.endElement(HTML.TR_ELEMENT);
+        holder.nextGroupRow();
+    }
+
+    protected boolean rowGroupChanged(FacesContext context,
+            ExtendedTableHolder holder) {
+        UIExtendedDataTable table = holder.getTable();
+        if (holder.getLastData() == null)
+            return true;
+
+        // get sort fields
+        List<SortField2> sortFields = table.getSortFields();
+        // get group field which is actually the first from sort fields
+        List<SortField2> groupFields = new ArrayList<SortField2>();
+        if (!sortFields.isEmpty())
+            groupFields.add(sortFields.get(0));
+        // create wrapper factory
+        ObjectWrapperFactory wrapperFactory = new ObjectWrapperFactory(context,
+                table.getVar(), groupFields);
+        // create wrapper for last data
+        JavaBeanWrapper wrappedLstD = wrapperFactory.wrapObject(holder
+                .getLastData());
+        // create wrapper for current data
+        JavaBeanWrapper wrappedActD = wrapperFactory.wrapObject(table
+                .getRowData());
+        // create comparator
+        WrappedBeanComparator2 wrappedBeanComparator = new WrappedBeanComparator2(
+                (groupFields));
+        // compare last and current data
+        return (wrappedBeanComparator.compare(wrappedLstD, wrappedActD) != 0);
+    }// rowGroupChanged
+
+    public void encodeOneRow(FacesContext context, ExtendedTableHolder holder)
+            throws IOException {
+        UIExtendedDataTable table = holder.getTable();
+        ResponseWriter writer = context.getResponseWriter();
+        Iterator<UIColumn> iter = table.getSortedColumns();
+        boolean first = true;
+        int currentColumn = 0;
+        UIColumn column = null;
+        if (holder.isGroupingOn() && (rowGroupChanged(context, holder))) {
+            if (this.firstRow) {
+                encodeFakeIeRow(context, table, holder);
+            }
+            encodeGroupRow(context, table, holder);
+        }
+        this.firstRow = false;
+        while (iter.hasNext()) {
+            column = iter.next();
+            // Start new row for first column - expect a case of the detail
+            // table, wich will be insert own row.
+            boolean isRow = (column instanceof Row);
+            if (first && !isRow) {
+                encodeRowStart(context, getFirstRowSkinClass(), holder
+                        .getRowClass(), table, holder, writer);
+            }
+
+            // TODO PKA CHANGE COLUMN RENDERER TO GET RID OF && false
+            if (false && (column instanceof Column)) {
+                boolean breakBefore = ((Column) column).isBreakBefore()
+                        || isRow;
+                if (breakBefore && !first) {
+                    // close current row
+                    writer.endElement(HTML.TR_ELEMENT);
+                    // reset columns counter.
+                    currentColumn = 0;
+                    // Start new row, expect a case of the detail table, wich
+                    // will be insert own row.
+                    if (!isRow) {
+                        holder.nextRow();
+                        encodeRowStart(context, holder.getRowClass(), table,
+                                holder, writer);
+                    }
+                }
+                encodeCellChildren(context, column,
+                        first ? getFirstRowSkinClass() : null,
+                        getRowSkinClass(), holder.getRowClass(),
+                        getCellSkinClass(), holder
+                                .getColumnClass(currentColumn));
+                // renderChild(context, column);
+                if (isRow && iter.hasNext()) {
+                    // Start new row for remained columns.
+                    holder.nextRow();
+                    encodeRowStart(context, holder.getRowClass(), table,
+                            holder, writer);
+                    // reset columns counter.
+                    currentColumn = -1;
+                }
+            } else if (column.isRendered()) {
+                // UIColumn don't have own renderer
+                writer.startElement(HTML.td_ELEM, table);
+                getUtils().encodeId(context, column);
+                encodeStyleClass(writer, null, getCellSkinClass(), null, null);
+                // TODO - encode column attributes.
+                writer.startElement(HTML.DIV_ELEM, table);
+                writer.writeAttribute("class", "dt-cell-div", null);
+                // writer.writeAttribute("class", "dt-sdt-bcbody", null); //KAW
+                // IE
+                renderChildren(context, column);
+                writer.endElement(HTML.DIV_ELEM);
+                writer.endElement(HTML.td_ELEM);
+            }
+            currentColumn++;
+            first = false;
+        }
+        // encode additional empty row for resizing
+        writer.startElement(HTML.td_ELEM, table);
+        String columnClass = holder.getColumnClass(currentColumn);
+        encodeStyleClass(writer, null, getCellSkinClass(), null, columnClass);
+        writer.startElement(HTML.DIV_ELEM, table);
+        writer.writeAttribute("class", "dt-cell-div", null);
+        writer.endElement(HTML.DIV_ELEM);
+        writer.endElement(HTML.td_ELEM);
+        // Close row if then is open.
+        if (!first && !(column instanceof Row)) {
+            writer.endElement(HTML.TR_ELEMENT);
+        }
+    }
+
+    protected void encodeRowStart(FacesContext context, String rowClass,
+            UIDataTable table, TableHolder holder, ResponseWriter writer)
+            throws IOException {
+        encodeRowStart(context, getRowSkinClass(), rowClass, table, holder,
+                writer);
+    }
+
+    /**
+     * @return
+     */
+    protected String getRowSkinClass() {
+        return "dr-table-row rich-table-row";
+    }
+
+    /**
+     * @return
+     */
+    protected String getFirstRowSkinClass() {
+        return "dr-table-firstrow rich-table-firstrow";
+        // return "dt-sdt-rb rich-table-firstrow"; //KAW IE
+    }
+
+    /**
+     * @return
+     */
+    protected String getCellSkinClass() {
+        return "dr-table-cell rich-table-cell";
+        // return "dt-sdt-bc rich-table-cell"; //KAW IE
+
+    }
+
+    protected void encodeRowStart(FacesContext context, String skinClass,
+            String rowClass, UIDataTable table, TableHolder holder,
+            ResponseWriter writer) throws IOException {
+        writer.startElement(HTML.TR_ELEMENT, table);
+        encodeRowId(context, writer, table, holder.getRowCounter());
+        encodeStyleClass(writer, null, skinClass, null, rowClass);
+        encodeRowEvents(context, table);
+    }
+
+    /**
+     * Row ID generator
+     * 
+     * @param context
+     *            current FacesContext
+     * @param writer
+     *            ResponseWriter for FacesContext
+     * @param table
+     *            table for which this row is being encoded
+     * @param rowId
+     *            new rowId to encode
+     * @throws IOException
+     *             if ResponseWriter fails
+     */
+    protected void encodeRowId(FacesContext context, ResponseWriter writer,
+            UIDataTable table, int rowId) throws IOException {
+        String[] ownersId = table.getClientId(context).split(":");
+        String ownerId = ownersId[0] + ":" + ownersId[1];
+        getUtils().writeAttribute(writer, "id", ownerId + ":n:" + rowId);
+    }
+
+    /**
+     * Calculate total number of columns in table.
+     * 
+     * @param context
+     * @param table
+     * @return
+     */
+    protected int getColumnsCount(UIDataTable table) {
+        int count = 0;
+        // check for exact value in component
+        Integer span = (Integer) table.getAttributes().get("columns");
+        if (null != span && span.intValue() != Integer.MIN_VALUE) {
+            count = span.intValue();
+        } else {
+            // calculate max html columns count for all columns/rows children.
+            Iterator<UIComponent> col = table.columns();
+            count = calculateRowColumns(col);
+        }
+        return count;
+    }
+
+    /**
+     * Calculate max number of columns per row. For rows, recursive calculate
+     * max length.
+     * 
+     * @param col -
+     *            Iterator other all columns in table.
+     * @return
+     */
+    protected int calculateRowColumns(Iterator<UIComponent> col) {
+        int count = 0;
+        int currentLength = 0;
+        while (col.hasNext()) {
+            UIComponent column = (UIComponent) col.next();
+            if (column.isRendered()) {
+                if (column instanceof Row) {
+                    // Store max calculated value of previsous rows.
+                    if (currentLength > count) {
+                        count = currentLength;
+                    }
+                    // Calculate number of columns in row.
+                    currentLength = calculateRowColumns(((Row) column)
+                            .columns());
+                    // Store max calculated value
+                    if (currentLength > count) {
+                        count = currentLength;
+                    }
+                    currentLength = 0;
+                } else if (column instanceof Column) {
+                    Column tableColumn = (Column) column;
+                    // For new row, save length of previsous.
+                    if (tableColumn.isBreakBefore()) {
+                        if (currentLength > count) {
+                            count = currentLength;
+                        }
+                        currentLength = 0;
+                    }
+                    Integer colspan = (Integer) column.getAttributes().get(
+                            "colspan");
+                    // Append colspan of this column
+                    if (null != colspan
+                            && colspan.intValue() != Integer.MIN_VALUE) {
+                        currentLength += colspan.intValue();
+                    } else {
+                        currentLength++;
+                    }
+                } else if (column instanceof javax.faces.component.UIColumn) {
+                    // UIColumn always have colspan == 1.
+                    currentLength++;
+                }
+
+            }
+        }
+        if (currentLength > count) {
+            count = currentLength;
+        }
+        return count;
+    }
+
+    public void encodeScriptIfNecessary(FacesContext context,
+            UIExtendedDataTable table) throws IOException {
+        boolean shouldRender = false;
+        Iterator<UIColumn> columns = table.getSortedColumns();
+        while (columns.hasNext() && !shouldRender) {
+            UIColumn next = columns.next();
+            shouldRender = next.isSortable();
+            shouldRender = true;// shouldRender || (next instanceof
+            // HtmlDataColumn);
+        }
+        shouldRender = true;
+        if (shouldRender) {
+            getUtils().writeScript(context, table,
+                    createClientDataTable(context, table));
+        }
+    }
+
+    public String createClientDataTable(FacesContext context,
+            UIExtendedDataTable table) {
+        JSFunction function = new JSFunction("new ExtendedDataTable.DataTable");
+        function.addParameter(table.getBaseClientId(context));
+        ScriptOptions scriptOptions = new ScriptOptions(table);
+        // add on resize column AJAX function
+        scriptOptions.addOption(ON_RESIZE_FUNCTION, getOnResizeFunctionDef(
+                context, table));
+        scriptOptions.addOption(SORT_FUNCTION, getSortFunctionDef(context,
+                table));
+        scriptOptions.addOption(SHOW_MENU_FUNCTION, getShowMenuFunction(
+                context, table));
+        /* Not needed if we do not save open/close state */
+        scriptOptions.addOption(ON_GROUP_TOGGLE_FUNCTION,
+                getOnGroupToggleFunctionDef(context, table));
+        scriptOptions.addOption("minColumnWidth", 100);
+        composite.mergeScriptOptions(scriptOptions, context, table);
+        function.addParameter(scriptOptions);
+        return function.toScript();
+    }
+
+    protected JSFunctionDefinition getShowMenuFunction(FacesContext context,
+            UIDataTable table) {
+        return new RichTableMenuRenderer().createShowMenuEventFunction();
+    }
+
+    protected JSFunctionDefinition getSortFunctionDef(FacesContext context,
+            UIDataTable table) {
+        return getSortFunctionDef(context, table, null);
+    }
+
+    protected JSFunctionDefinition getSortFunctionDef(FacesContext context,
+            UIDataTable table, Boolean asc) {
+        JSFunctionDefinition definition = new JSFunctionDefinition();
+        definition.addParameter("event");
+        definition.addParameter("columnId");
+        String id = table.getClientId(context);
+        Map<String, Object> eventOptions = AjaxRendererUtils.buildEventOptions(
+                context, table);
+        @SuppressWarnings("unchecked")
+        Map<String, Object> parameters = (Map<String, Object>) eventOptions
+                .get("parameters");
+        parameters.put(id, SORT_FILTER_PARAMETER);
+        if (asc != null) {
+            parameters.put(SORT_DIR_PARAMETER, asc ? SORT_DIR_PARAMETER_ASC
+                    : SORT_DIR_PARAMETER_DESC);
+        }
+        // parameters.put(SORT_FILTER_PARAMETER, column.getClientId(context));
+        JSFunctionDefinition onAjaxCompleteFunction = getOnAjaxCompleteFunction(
+                context, table);
+        if (onAjaxCompleteFunction != null) {
+            eventOptions.put(AjaxRendererUtils.ONCOMPLETE_ATTR_NAME,
+                    onAjaxCompleteFunction);
+        }
+        definition.addToBody("var options = ").addToBody(
+                ScriptUtils.toScript(eventOptions)).addToBody(";\n");
+        definition.addToBody("options.parameters['" + SORT_FILTER_PARAMETER
+                + "'] = columnId;\n");
+        JSFunction ajaxFunction = AjaxRendererUtils.buildAjaxFunction(table,
+                context);
+        ajaxFunction.addParameter(new JSReference("options"));
+        definition.addToBody(ajaxFunction.toScript()).addToBody(";\n");
+        return definition;
+    }// getSortFunction
+
+    protected JSFunction getSortFunction(FacesContext context, UIDataTable table) {
+        String id = table.getClientId(context);
+        Map<String, Object> requestOpts = AjaxRendererUtils.buildEventOptions(
+                context, table);
+        @SuppressWarnings("unchecked")
+        Map<String, Object> parameters = (Map<String, Object>) requestOpts
+                .get("parameters");
+        parameters.put(id, SORT_FILTER_PARAMETER);
+        parameters.put(SORT_DIR_PARAMETER, "{sortDirection}");
+        parameters.put(SORT_FILTER_PARAMETER, "{columnId}");
+        JSFunctionDefinition onAjaxCompleteFunction = getOnAjaxCompleteFunction(
+                context, table);
+        if (onAjaxCompleteFunction != null) {
+            requestOpts.put(AjaxRendererUtils.ONCOMPLETE_ATTR_NAME,
+                    onAjaxCompleteFunction);
+        }
+        JSFunction ajaxFunction = AjaxRendererUtils.buildAjaxFunction(table,
+                context);
+        ajaxFunction.addParameter(requestOpts);
+        return ajaxFunction;
+    }// getSortFunction
+
+    protected JSFunctionDefinition getOnGroupToggleFunctionDef(
+            FacesContext context, UIDataTable table) {
+        JSFunctionDefinition definition = new JSFunctionDefinition();
+        definition.addParameter("event");
+        definition.addParameter("groupIndex");
+        Map<String, Object> eventOptions = AjaxRendererUtils.buildEventOptions(
+                context, table);
+        @SuppressWarnings("unchecked")
+        Map<String, Object> parameters = (Map<String, Object>) eventOptions
+                .get("parameters");
+        parameters.put(GROUP_TOGGLE_ACTION_NAME, GROUP_TOGGLE_ACTION_NAME);
+        definition.addToBody("var options = ").addToBody(
+                ScriptUtils.toScript(eventOptions)).addToBody(";\n");
+        definition
+                .addToBody("options.parameters['groupIndex'] = groupIndex;\n");
+        JSFunction ajaxFunction = AjaxRendererUtils.buildAjaxFunction(table,
+                context);
+        ajaxFunction.addParameter(new JSReference("options"));
+        definition.addToBody(ajaxFunction.toScript()).addToBody(";\n");
+        return definition;
+    }// getSortFunction
+
+    protected JSFunction getGroupFunction(FacesContext context,
+            UIDataTable table) {
+        String id = table.getClientId(context);
+        Map<String, Object> requestOpts = AjaxRendererUtils.buildEventOptions(
+                context, table);
+        @SuppressWarnings("unchecked")
+        Map<String, Object> parameters = (Map<String, Object>) requestOpts
+                .get("parameters");
+        parameters.put(id, GROUP_FILTER_PARAMETER);
+        parameters.put(GROUP_FILTER_PARAMETER, "{columnId}");
+        JSFunctionDefinition onAjaxCompleteFunction = getOnAjaxCompleteFunction(
+                context, table);
+        if (onAjaxCompleteFunction != null) {
+            requestOpts.put(AjaxRendererUtils.ONCOMPLETE_ATTR_NAME,
+                    onAjaxCompleteFunction);
+        }
+        JSFunction ajaxFunction = AjaxRendererUtils.buildAjaxFunction(table,
+                context);
+        ajaxFunction.addParameter(requestOpts);
+        return ajaxFunction;
+    }// getSortFunction
+
+    protected JSFunctionDefinition getOnResizeFunctionDef(FacesContext context,
+            UIDataTable table) {
+        JSFunctionDefinition definition = new JSFunctionDefinition();
+        definition.addParameter("event");
+        definition.addParameter("columnWidths");
+
+        Map<String, Object> eventOptions = AjaxRendererUtils.buildEventOptions(
+                context, table);
+        @SuppressWarnings("unchecked")
+        Map<String, Object> parameters = (Map<String, Object>) eventOptions
+                .get("parameters");
+        parameters.put(COL_RESIZE_ACTION_NAME, COL_RESIZE_ACTION_NAME);
+        definition.addToBody("var options = ").addToBody(
+                ScriptUtils.toScript(eventOptions)).addToBody(";\n");
+        definition
+                .addToBody("options.parameters['columnWidths'] = columnWidths;\n");
+
+        JSFunction ajaxFunction = AjaxRendererUtils.buildAjaxFunction(table,
+                context);
+        ajaxFunction.addParameter(new JSReference("options"));
+        definition.addToBody(ajaxFunction.toScript()).addToBody(";\n");
+        return definition;
+    }
+
+    protected JSFunction getChangeColumnVisibilityFunction(
+            FacesContext context, UIDataTable table) {
+        boolean ajaxSingle = true;
+        Map<String, Object> requestOpts = AjaxRendererUtils.buildEventOptions(
+                context, table);
+
+        JSFunctionDefinition onAjaxCompleteFunction = getOnAjaxCompleteFunction(
+                context, table);
+        if (onAjaxCompleteFunction != null)
+            requestOpts.put(AjaxRendererUtils.ONCOMPLETE_ATTR_NAME,
+                    onAjaxCompleteFunction);
+
+        @SuppressWarnings("unchecked")
+        Map<String, Object> parameters = (Map<String, Object>) requestOpts
+                .get("parameters");
+        if (parameters == null) {
+            parameters = new HashMap<String, Object>();
+            requestOpts.put("parameters", parameters);
+        }
+        if (ajaxSingle) {
+            if (!parameters
+                    .containsKey(AjaxRendererUtils.AJAX_SINGLE_PARAMETER_NAME))
+                parameters.put(AjaxRendererUtils.AJAX_SINGLE_PARAMETER_NAME,
+                        table.getClientId(context));
+            if (!requestOpts.containsKey("control"))
+                requestOpts.put("control", JSReference.THIS);
+        }
+        parameters.put(
+                table.getClientId(context) + ":" + CHANGE_COL_VISIBILITY,
+                "{columnId}");
+
+        JSFunction ajaxFunction = AjaxRendererUtils.buildAjaxFunction(table,
+                context);
+        ajaxFunction.addParameter(requestOpts);
+        return ajaxFunction;
+    }
+
+    protected JSFunction getPreSendAjaxRequestFunction(FacesContext context,
+            UIDataTable table) {
+        return new JSFunction(getJavaScriptVarName(context, table)
+                + ".preSendAjaxRequest");
+    }
+
+    protected void preDecode(FacesContext context, UIComponent component) {
+        if (component instanceof UIExtendedDataTable) {
+            UIExtendedDataTable table = (UIExtendedDataTable) component;
+            table.ensureTableStateInitialized();
+        }
+        super.preDecode(context, component);
+    }
+
+    protected void preEncodeBegin(FacesContext context, UIComponent component)
+            throws IOException {
+        if (component instanceof UIExtendedDataTable) {
+            UIExtendedDataTable table = (UIExtendedDataTable) component;
+
+            for (Iterator<UIColumn> columns = table.getChildColumns(); columns
+                    .hasNext();) {
+                UIColumn column = columns.next();
+                column.setId(column.getId());
+            }
+
+            table.ensureTableStateInitialized();
+        }
+        super.preEncodeBegin(context, component);
+    }
+
+    protected void doDecode(FacesContext context, UIComponent component) {
+        super.doDecode(context, component);
+        composite.decode(context, component);
+
+        if (component instanceof UIExtendedDataTable) {
+            UIExtendedDataTable table = (UIExtendedDataTable) component;
+            Map<String, String> map = context.getExternalContext()
+                    .getRequestParameterMap();
+            String clientId = component.getClientId(context);
+
+            if (SORT_FILTER_PARAMETER.equals(map.get(clientId))) {
+                String sortColumnId = map.get(SORT_FILTER_PARAMETER);
+                boolean isSingleSortMode = !"multi".equals(table.getSortMode());
+
+                boolean isGroupingOn = table.isGroupingOn();
+                boolean sortByGroupingColumn = false;
+                // String groupingColumnId = null;
+                UIColumn groupingColumn = null;
+                if (isGroupingOn) {
+                    groupingColumn = table.getGroupingColumn();
+                    sortByGroupingColumn = (groupingColumn == null ? false
+                            : groupingColumn.getClientId(context).equals(
+                                    sortColumnId));
+                    // groupingColumnId = table.getGroupingColumnId();
+                }
+                for (Iterator<UIColumn> columns = table.getChildColumns(); columns
+                        .hasNext();) {
+                    UIColumn column = columns.next();
+                    String id = column.getId();
+                    column.setId(id);
+
+                    if (sortColumnId != null) {
+                        boolean isGroupingColumn = (isGroupingOn && column
+                                .equals(groupingColumn));
+                        if (sortColumnId.equals(column.getClientId(context))) {
+                            // set sort order
+                            if (map.containsKey(SORT_DIR_PARAMETER)) {
+                                String sortDir = (String) map
+                                        .get(SORT_DIR_PARAMETER);
+                                column
+                                        .setSortOrder((sortDir
+                                                .equals(SORT_DIR_PARAMETER_ASC) ? Ordering.ASCENDING
+                                                : (sortDir
+                                                        .equals(SORT_DIR_PARAMETER_DESC) ? Ordering.DESCENDING
+                                                        : Ordering.UNSORTED)));
+                            } else {
+                                column.toggleSortOrder();
+                            }
+
+                            Collection<Object> sortPriority = table
+                                    .getSortPriority();
+                            // clear sort priority in case of single sort mode
+                            if (isSingleSortMode) {
+                                sortPriority.clear();
+                            }
+                            // add column to sort priority if is not added yet
+                            if (!sortPriority.contains(id)) {
+                                sortPriority.add(id);
+                            }
+                            if (isGroupingColumn) {
+                                // set as grouping column to mark that grouping
+                                // order has changed
+                                table.setGroupingColumn(column);
+                            }
+                        } else if (isSingleSortMode) { // in case of single
+                                                        // sort mode
+                            if (!isGroupingColumn) { // grouping is not by
+                                                        // this column
+                                if (!sortByGroupingColumn) {// sort not by
+                                                            // grouping column
+                                    // disable sort by this column
+                                    column.setSortOrder(Ordering.UNSORTED);
+                                }
+                            }
+                        }
+                    }
+
+                    UIInput filterValueInput = (UIInput) column
+                            .getFacet(FILTER_INPUT_FACET_NAME);
+                    if (null != filterValueInput) {
+                        filterValueInput.decode(context);
+                        String oldFilterValue = column.getFilterValue();
+                        Object submittedValue = filterValueInput
+                                .getSubmittedValue();
+                        String newFilterValue = null;
+                        if (null != submittedValue) {
+                            newFilterValue = filterValueInput
+                                    .getSubmittedValue().toString();
+                            if ((newFilterValue != null)
+                                    && (newFilterValue.length() == 0)) {
+                                newFilterValue = null;
+                            }
+                            column.setFilterValue(newFilterValue);
+                        }
+                        boolean filterChanged = (newFilterValue == null ? (oldFilterValue != null)
+                                : !newFilterValue.equals(oldFilterValue));
+                        if (filterChanged) {
+                            table.resetGroupVisibilityState();
+                        }
+                    }
+                }
+
+                AjaxContext.getCurrentInstance().addComponentToAjaxRender(
+                        component);
+                // AjaxContext.getCurrentInstance().addRenderedArea(clientId +
+                // ":tu");
+            }
+
+            // GROUP COLUMNS
+            if (GROUP_FILTER_PARAMETER.equals(map.get(clientId))) {
+                String groupColumnId = map.get(GROUP_FILTER_PARAMETER);
+                // turn off grouping
+                table.disableGrouping();
+                if (groupColumnId != null) {
+                    // turn off sorting by all columns
+                    table.getSortPriority().clear();
+                    for (Iterator<UIColumn> columns = table.getChildColumns(); columns
+                            .hasNext();) {
+                        UIColumn column = columns.next();
+                        // child.setId(child.getId());
+                        if (groupColumnId.equals(column.getClientId(context))) { // group
+                                                                                    // by
+                                                                                    // this
+                                                                                    // column
+                            // set sort order if is not set
+                            if (column.getSortOrder().equals(Ordering.UNSORTED)) {
+                                column.setSortOrder(Ordering.ASCENDING);
+                            }
+                            // set as grouping column
+                            table.setGroupingColumn(column);
+                        } else { // grouping is not by this column
+                            // turn off sorting by this column
+                            column.setSortOrder(Ordering.UNSORTED);
+                        }
+                    }// for columns
+                }// if
+
+                AjaxContext.getCurrentInstance().addComponentToAjaxRender(
+                        component);
+                // AjaxContext.getCurrentInstance().addRenderedArea(clientId +
+                // ":tu");
+            }// group columns
+
+            // CHANGE COLUMN ORDER - DRAG AND DROP
+            String dragSourceId = (String) map
+                    .get(org.richfaces.renderkit.DraggableRendererContributor.DRAG_SOURCE_ID);
+            String dropTargetId = (String) map
+                    .get(org.richfaces.renderkit.DropzoneRendererContributor.DROP_TARGET_ID);
+            if ((dragSourceId != null) && (dropTargetId != null)) {
+                Pattern sourcePattern = Pattern.compile(clientId + ":(\\w*):"
+                        + TableDragDropRenderer.DRAG_SOURCE_SCRIPT_ID);
+                Pattern targetPattern = Pattern.compile(clientId + ":(\\w*):"
+                        + TableDragDropRenderer.DROP_TARGET_SCRIPT_ID + "("
+                        + TableDragDropRenderer.DROP_TARGET_BEFORE + "|"
+                        + TableDragDropRenderer.DROP_TARGET_AFTER + ")");
+                Matcher sourceMatcher = sourcePattern.matcher(dragSourceId);
+                Matcher targetMatcher = targetPattern.matcher(dropTargetId);
+                if (sourceMatcher.find() && targetMatcher.find()) {
+                    String sourceColumnId = sourceMatcher.group(1);
+                    String targetColumnId = targetMatcher.group(1);
+                    String kind = targetMatcher.group(2);
+
+                    DragDropEvent dragDropEvent = new DragDropEvent(component);
+                    dragDropEvent.setDragValue(sourceColumnId);
+                    dragDropEvent.setDropValue(targetColumnId);
+                    dragDropEvent.setDropBefore(kind
+                            .equals(TableDragDropRenderer.DROP_TARGET_BEFORE));
+
+                    dragDropEvent.queue();
+
+                    AjaxContext.getCurrentInstance().addComponentToAjaxRender(
+                            component);
+                    // AjaxContext ajaxContext =
+                    // AjaxContext.getCurrentInstance();
+                    // ajaxContext.addComponentToAjaxRender(component);
+                    // ajaxContext.addRenderedArea(clientId + ":tb");// body
+                    // ajaxContext.addRenderedArea(clientId + ":tu");
+                    // ajaxContext.addRenderedArea(clientId + ":tm");// menu
+                    // AjaxContext.getCurrentInstance().addRenderedArea(clientId);
+                }
+            }// change column order
+
+            // CHANGE COLUMN VISIBILITY
+            String columnToChange = (String) map.get(clientId + ":"
+                    + TableMenuRenderer.CHANGE_COL_VISIBILITY);
+            if (columnToChange != null) {
+                ChangeColumnVisibilityEvent event = new ChangeColumnVisibilityEvent(
+                        component, columnToChange);
+
+                event.queue();
+
+                AjaxContext.getCurrentInstance().addComponentToAjaxRender(
+                        component);
+                // AjaxContext ajaxContext = AjaxContext.getCurrentInstance();
+                // ajaxContext.addComponentToAjaxRender(component);
+                // ajaxContext.addRenderedArea(clientId + ":tb");// body
+                // ajaxContext.addRenderedArea(clientId + ":tu");
+                // ajaxContext.addRenderedArea(clientId + ":tm");// menu
+            }// change column visibility
+
+            // COLUMN RESIZE
+            if (COL_RESIZE_ACTION_NAME.equals(map.get(COL_RESIZE_ACTION_NAME))) {
+                String colWidths = (String) map.get("columnWidths");
+                ColumnResizeEvent event = new ColumnResizeEvent(component,
+                        colWidths);
+                event.queue();
+            }
+
+            // TOGGLE ROW GROUP
+            if (GROUP_TOGGLE_ACTION_NAME.equals(map
+                    .get(GROUP_TOGGLE_ACTION_NAME))) {
+                String group = (String) map.get("groupIndex");
+                if (group != null) {
+                    try {
+                        table.toggleGroup(Integer.valueOf(group));
+                    } catch (NumberFormatException _) {
+                    }
+                }// if
+            }
+
+        }
+    }
+
+    public void encodeEnd(FacesContext context, UIComponent component)
+            throws IOException {
+        super.encodeEnd(context, component);
+        String clientId = component.getClientId(context);
+        Set<String> ajaxRenderedAreas = AjaxContext.getCurrentInstance()
+                .getAjaxRenderedAreas();
+        // if (ajaxRenderedAreas.contains(clientId + ":tb")) {
+        // ajaxRenderedAreas.remove(clientId);
+        // }
+        if (ajaxRenderedAreas.contains(clientId)) {
+            // remove all child elements
+            for (Iterator<String> iter = ajaxRenderedAreas.iterator(); iter
+                    .hasNext();) {
+                String area = iter.next();
+                if (area.startsWith(clientId) && (!area.equals(clientId))) {
+                    iter.remove();
+                }
+            }
+        }
+    }
+
+    protected void addInplaceInput(FacesContext context, UIComponent column,
+            String buffer) throws IOException {
+        UIInput filterValueInput = (UIInput) column
+                .getFacet(FILTER_INPUT_FACET_NAME);
+        if (null == filterValueInput) {
+            filterValueInput = (UIInput) context.getApplication()
+                    .createComponent(UIInput.COMPONENT_TYPE);
+            filterValueInput.setId(column.getId() + SORT_FILTER_PARAMETER);
+            filterValueInput.setImmediate(true);
+            column.getFacets().put(FILTER_INPUT_FACET_NAME, filterValueInput);
+            filterValueInput.getAttributes().put(HTML.onclick_ATTRIBUTE,
+                    "Event.stop(event);");
+        }
+        String filterEvent = (String) column.getAttributes().get("filterEvent");
+        if (null == filterEvent || "".equals(filterEvent)) {
+            filterEvent = "onchange";
+        }
+
+        filterValueInput.getAttributes().put(filterEvent, buffer);
+        filterValueInput.setValue(column.getAttributes().get("filterValue"));
+
+        getUtils().encodeBeginFormIfNessesary(context, column);
+        renderChild(context, filterValueInput);
+        getUtils().encodeEndFormIfNessesary(context, column);
+    }
+
+    protected String buildAjaxFunction(FacesContext context,
+            UIComponent column, boolean sortable,
+            JSFunctionDefinition onAjaxCompleteFunction) {
+        UIComponent table = column.getParent();
+        String id = table.getClientId(context);
+        JSFunction ajaxFunction = AjaxRendererUtils.buildAjaxFunction(table,
+                context);
+        Map<String, Object> eventOptions = AjaxRendererUtils.buildEventOptions(
+                context, table);
+        @SuppressWarnings("unchecked")
+        Map<String, Object> parameters = (Map<String, Object>) eventOptions
+                .get("parameters");
+
+        parameters.put(id, SORT_FILTER_PARAMETER);
+        if (sortable) {
+            parameters.put(SORT_FILTER_PARAMETER, column.getClientId(context));
+        }
+        if (onAjaxCompleteFunction != null)
+            eventOptions.put(AjaxRendererUtils.ONCOMPLETE_ATTR_NAME,
+                    onAjaxCompleteFunction);
+        ajaxFunction.addParameter(eventOptions);
+        StringBuffer buffer = new StringBuffer();
+        ajaxFunction.appendScript(buffer);
+
+        return buffer.toString();
+    }
+
+    protected class SimpleHeaderEncodeStrategy implements HeaderEncodeStrategy {
+
+        public void encodeBegin(FacesContext context, ResponseWriter writer,
+                UIComponent column, String facetName, boolean sortableColumn)
+                throws IOException {
+
+        }
+
+        public void encodeEnd(FacesContext context, ResponseWriter writer,
+                UIComponent column, String facetName, boolean sortableColumn)
+                throws IOException {
+
+        }
+
+    }
+
+    protected class RichHeaderEncodeStrategy implements HeaderEncodeStrategy {
+
+        public void encodeBegin(FacesContext context, ResponseWriter writer,
+                UIComponent column, String facetName, boolean sortableColumn)
+                throws IOException {
+            UIColumn col = (UIColumn) column;
+            String clientId = col.getClientId(context) + facetName;
+            writer.writeAttribute("id", clientId, null);
+
+            if (sortableColumn && col.isSelfSorted()) {
+                writer.writeAttribute(HTML.onclick_ATTRIBUTE,
+                        buildAjaxFunction(context, column, true, null)
+                                .toString(), null);
+                writer.writeAttribute(HTML.style_ATTRIBUTE, "cursor: pointer;",
+                        null);
+            }
+
+            writer.startElement(HTML.DIV_ELEM, column);
+            writer.writeAttribute(HTML.id_ATTRIBUTE, clientId + ":sortDiv",
+                    null);
+            AjaxContext.getCurrentInstance().addRenderedArea(
+                    clientId + ":sortDiv");
+
+            if (sortableColumn) {
+                writer.startElement(HTML.SPAN_ELEM, column);
+                writer.writeAttribute(HTML.class_ATTRIBUTE,
+                        "dr-table-sortable-header", null);
+            }
+        }
+
+        public void encodeEnd(FacesContext context, ResponseWriter writer,
+                UIComponent column, String facetName, boolean sortableColumn)
+                throws IOException {
+            UIColumn col = (UIColumn) column;
+            if (sortableColumn) {
+                String imageUrl = null;
+                if (Ordering.ASCENDING.equals(col.getSortOrder())) {
+                    if (null != col.getSortIconAscending()) {
+                        imageUrl = col.getSortIconAscending();
+                    } else {
+                        imageUrl = getResource(TriangleIconUp.class.getName())
+                                .getUri(context, null);
+                    }
+                } else if (Ordering.DESCENDING.equals(col.getSortOrder())) {
+                    if (null != col.getSortIconDescending()) {
+                        imageUrl = col.getSortIconDescending();
+                    } else {
+                        imageUrl = getResource(TriangleIconDown.class.getName())
+                                .getUri(context, null);
+                    }
+                } else if (col.isSelfSorted()) {
+                    if (null != col.getSortIcon()) {
+                        imageUrl = col.getSortIcon();
+                    } else {
+                        imageUrl = getResource(
+                                DataTableIconSortNone.class.getName()).getUri(
+                                context, null);
+                    }
+                }
+
+                if (imageUrl != null) {
+                    writer.startElement(HTML.IMG_ELEMENT, column);
+                    writer.writeAttribute(HTML.src_ATTRIBUTE, imageUrl, null);
+                    writer.writeAttribute(HTML.width_ATTRIBUTE, "15", null);
+                    writer.writeAttribute(HTML.height_ATTRIBUTE, "15", null);
+                    writer.writeAttribute(HTML.class_ATTRIBUTE,
+                            "dr-table-header-sort-img", null);
+                    writer.endElement(HTML.IMG_ELEMENT);
+                }
+                writer.endElement(HTML.SPAN_ELEM);
+            }
+
+            writer.endElement(HTML.DIV_ELEM);
+
+            if (col.getFilterMethod() == null
+                    && col.getValueExpression("filterExpression") == null
+                    && col.getValueExpression("filterBy") != null) {
+
+                writer.startElement(HTML.DIV_ELEM, column);
+                addInplaceInput(context, column, buildAjaxFunction(context,
+                        column, false, null));
+                writer.endElement(HTML.DIV_ELEM);
+            }
+        }
+    }
+
+    protected class G3HeaderEncodeStrategy implements HeaderEncodeStrategy {
+
+        public void encodeBegin(FacesContext context, ResponseWriter writer,
+                UIComponent column, String facetName, boolean sortableColumn)
+                throws IOException {
+            if (column instanceof UIColumn) {
+                UIColumn dataColumn = (UIColumn) column;
+                String clientId = dataColumn.getClientId(context);// +
+                                                                    // facetName;
+                writer.writeAttribute("id", clientId, null);
+                column.getAttributes().put("columnClientId", clientId);
+                boolean sortable = sortableColumn && dataColumn.isSelfSorted();
+                if (sortable) {
+                    /*
+                     * writer.writeAttribute(HTML.onclick_ATTRIBUTE,
+                     * buildAjaxFunction( context, column, true,
+                     * getOnAjaxCompleteFunction(context, (UIDataTable)
+                     * column.getParent())) .toString(), null);
+                     */
+                    writer.writeAttribute(HTML.style_ATTRIBUTE,
+                            "cursor: pointer;", null);
+                }
+                writer.writeAttribute("sortable", String.valueOf(sortable),
+                        null);
+                // column.getAttributes().put("sortable",Boolean.valueOf(sortable));
+                // drag source area
+                writer.startElement(HTML.DIV_ELEM, dataColumn);
+                writer.writeAttribute(HTML.id_ATTRIBUTE, dataColumn.getParent()
+                        .getClientId(context)
+                        + "_hdrag_" + dataColumn.getId(), null);
+
+                writer.startElement(HTML.DIV_ELEM, dataColumn);
+                writer.writeAttribute(HTML.id_ATTRIBUTE, clientId + ":sortDiv",
+                        null);
+                AjaxContext.getCurrentInstance().addRenderedArea(
+                        clientId + ":sortDiv");
+
+                if (sortableColumn) {
+                    writer.startElement(HTML.SPAN_ELEM, column);
+                    writer.writeAttribute(HTML.class_ATTRIBUTE,
+                            "dr-table-sortable-header", null);
+                }
+            }
+        }
+
+        public void encodeEnd(FacesContext context, ResponseWriter writer,
+                UIComponent column, String facetName, boolean sortableColumn)
+                throws IOException {
+            if (column instanceof UIColumn) {
+                UIColumn dataColumn = (UIColumn) column;
+                String clientId = dataColumn.getClientId(context) + facetName;
+                String tableId = dataColumn.getParent().getClientId(context);
+
+                if (sortableColumn) {
+                    String imageUrl = null;
+                    if (Ordering.ASCENDING.equals(dataColumn.getSortOrder())) {
+                        if (null != dataColumn.getSortIconAscending()) {
+                            imageUrl = dataColumn.getSortIconAscending();
+                        } else {
+                            imageUrl = getResource(
+                                    TriangleIconUp.class.getName()).getUri(
+                                    context, null);
+                        }
+                    } else if (Ordering.DESCENDING.equals(dataColumn
+                            .getSortOrder())) {
+                        if (null != dataColumn.getSortIconDescending()) {
+                            imageUrl = dataColumn.getSortIconDescending();
+                        } else {
+                            imageUrl = getResource(
+                                    TriangleIconDown.class.getName()).getUri(
+                                    context, null);
+                        }
+                    } else if (dataColumn.isSelfSorted()) {
+                        if (null != dataColumn.getSortIcon()) {
+                            imageUrl = dataColumn.getSortIcon();
+                        } else {
+                            imageUrl = getResource(
+                                    DataTableIconSortNone.class.getName())
+                                    .getUri(context, null);
+                        }
+                    }
+
+                    if (imageUrl != null) {
+                        writer.startElement(HTML.IMG_ELEMENT, column);
+                        writer.writeAttribute(HTML.src_ATTRIBUTE, imageUrl,
+                                null);
+                        writer.writeAttribute(HTML.width_ATTRIBUTE, "15", null);
+                        writer
+                                .writeAttribute(HTML.height_ATTRIBUTE, "15",
+                                        null);
+                        writer.writeAttribute(HTML.class_ATTRIBUTE,
+                                "dr-table-header-sort-img", null);
+                        writer.endElement(HTML.IMG_ELEMENT);
+                    }
+                    writer.endElement(HTML.SPAN_ELEM);
+                }
+
+                writer.endElement(HTML.DIV_ELEM);
+
+                // drag source area
+                writer.endElement(HTML.DIV_ELEM);
+                String dragSourceId = tableId + "_hdrag_" + dataColumn.getId();
+                String indicatorId = tableId + ":dataTable_indicator";
+                renderDragSupport(context, dataColumn, dragSourceId,
+                        indicatorId, dataColumn.getColumnLabel());
+
+                // separator area
+                writer.startElement(HTML.SPAN_ELEM, column);
+                writer.writeAttribute(HTML.id_ATTRIBUTE, clientId + ":sepSpan",
+                        null);
+                writer
+                        .writeAttribute(HTML.class_ATTRIBUTE, "dt-sdt-hsep",
+                                null);
+                writer.endElement(HTML.SPAN_ELEM);
+
+                // drop target area LEFT
+                String spanId = tableId + "_hdrop_" + dataColumn.getId()
+                        + "left";
+                writer.startElement(HTML.SPAN_ELEM, column);
+                writer.writeAttribute(HTML.id_ATTRIBUTE, spanId, null);
+                writer.writeAttribute(HTML.class_ATTRIBUTE, "dt-sdt-hdrop",
+                        null);
+                writer.writeAttribute(HTML.style_ATTRIBUTE,
+                        "visibility: hidden;", null);
+                writer.startElement(HTML.SPAN_ELEM, column);
+                writer.writeAttribute(HTML.class_ATTRIBUTE,
+                        "dt-sdt-hdrop-top dt-sdt-hdrop-top-left", null);
+                writer.writeAttribute(HTML.style_ATTRIBUTE,
+                        "visibility: hidden;", null);
+                writer.endElement(HTML.SPAN_ELEM);
+                writer.startElement(HTML.SPAN_ELEM, column);
+                writer.writeAttribute(HTML.class_ATTRIBUTE,
+                        "dt-sdt-hdrop-bottom dt-sdt-hdrop-bottom-left", null);
+                writer.writeAttribute(HTML.style_ATTRIBUTE,
+                        "visibility: hidden;", null);
+                writer.endElement(HTML.SPAN_ELEM);
+                writer.endElement(HTML.SPAN_ELEM);
+                renderDropSupport(context, dataColumn, spanId, true);
+
+                // drop target area RIGHT
+                spanId = tableId + "_hdrop_" + dataColumn.getId() + "right";
+                writer.startElement(HTML.SPAN_ELEM, column);
+                writer.writeAttribute(HTML.id_ATTRIBUTE, spanId, null);
+                writer.writeAttribute(HTML.class_ATTRIBUTE, "dt-sdt-hdrop",
+                        null);
+                writer.writeAttribute(HTML.style_ATTRIBUTE,
+                        "visibility: hidden;", null);
+                writer.startElement(HTML.SPAN_ELEM, column);
+                writer.writeAttribute(HTML.class_ATTRIBUTE,
+                        "dt-sdt-hdrop-top dt-sdt-hdrop-top-right", null);
+                writer.writeAttribute(HTML.style_ATTRIBUTE,
+                        "visibility: hidden;", null);
+                writer.endElement(HTML.SPAN_ELEM);
+                writer.startElement(HTML.SPAN_ELEM, column);
+                writer.writeAttribute(HTML.class_ATTRIBUTE,
+                        "dt-sdt-hdrop-bottom dt-sdt-hdrop-bottom-right", null);
+                writer.writeAttribute(HTML.style_ATTRIBUTE,
+                        "visibility: hidden;", null);
+                writer.endElement(HTML.SPAN_ELEM);
+                writer.endElement(HTML.SPAN_ELEM);
+                renderDropSupport(context, dataColumn, spanId, false);
+
+                // menu
+                if ("header".equals(facetName)) {
+                    writer.startElement(HTML.DIV_ELEM, column);
+                    writer.writeAttribute(HTML.class_ATTRIBUTE,
+                            "extdt-menu-div", null);
+                    writer.endElement(HTML.DIV_ELEM);
+                }
+
+                // if (dataColumn.getFilterMethod() == null
+                // && dataColumn.getValueExpression("filterExpression") == null
+                // && dataColumn.getValueExpression("filterBy") != null) {
+                //
+                // writer.startElement(HTML.DIV_ELEM, column);
+                // addInplaceInput(context, column, buildAjaxFunction(context,
+                // column, false, getOnAjaxCompleteFunction(context,
+                // (UIDataTable) column.getParent())));
+                // writer.endElement(HTML.DIV_ELEM);
+                // }
+            }
+        }
+    }
+
+    public void encodeDragDropChildScripts(FacesContext context,
+            UIComponent component) throws IOException {
+        TableDragDropRenderer.getInstance(context).encodeChildScripts(context,
+                component);
+    }
+
+    public void encodeNamespace(FacesContext context, UIComponent component)
+            throws IOException {
+        NSUtils.writeNameSpace(context, component);
+    }
+
+    public void renderDragSupport(FacesContext context, UIColumn column,
+            String dragSourceId, String indicatorId, String dragLabel)
+            throws IOException {
+        TableDragDropRenderer.getInstance(context).renderDragSupport(column,
+                dragSourceId, indicatorId, dragLabel);
+    }// renderDragSupport
+
+    public void renderDropSupport(FacesContext context, UIColumn column,
+            String dropTargetId, boolean before) throws IOException {
+        TableDragDropRenderer renderer = TableDragDropRenderer
+                .getInstance(context);
+        renderer.setOnAjaxCompleteFunctionDef(getOnAjaxCompleteFunction(
+                context, (UIDataTable) column.getParent()));
+        renderer.setPreSendAjaxRequestFunction(getPreSendAjaxRequestFunction(
+                context, (UIDataTable) column.getParent()));
+        renderer.renderDropSupport(column, dropTargetId, before);
+    }// renderDropSupport
+
+    public static String getJavaScriptVarName(FacesContext context,
+            UIDataTable grid) {
+        String id = grid.getBaseClientId(context);
+        String name = "ExtendedDataTable.DataTable_"
+                + id.replaceAll("[^A-Za-z0-9_]", "_");
+        // String name = "Richfaces_ScrollableGrid";
+        return "window." + name;
+    }
+
+    protected String getScriptContributions(FacesContext context,
+            UIDataTable grid) {
+        return composite.getScriptContributions(getJavaScriptVarName(context,
+                grid), context, grid);
+    }
+
+    protected JSFunctionDefinition getOnAjaxCompleteFunction(
+            FacesContext context, UIDataTable table) {
+
+        return null;
+        // JSFunctionDefinition function = new JSFunctionDefinition("request",
+        // "event", "data");
+        // String varName = getJavaScriptVarName(context, table);
+        // function.addToBody(varName + ".update();");
+        // return function;
+    }
+
+    public void encodeTableMenu(FacesContext context, UIExtendedDataTable table)
+            throws IOException {
+        AjaxContext ajaxContext = AjaxContext.getCurrentInstance();
+        TableMenuRenderer menuRenderer = new RichTableMenuRenderer();
+        menuRenderer.setSortFunction(getSortFunction(context, table));
+        menuRenderer.setGroupFunction(getGroupFunction(context, table));
+        menuRenderer
+                .setChangeColumnVisibilityFunction(getChangeColumnVisibilityFunction(
+                        context, table));
+        menuRenderer.setPrepareFunction(getPreSendAjaxRequestFunction(context,
+                table));
+        for (Iterator<UIColumn> colums = table.getSortedColumns(); colums
+                .hasNext();) {
+            UIColumn col = colums.next();
+            if (col instanceof UIColumn) {
+                UIColumn column = (UIColumn) col;
+                // if (column.isRendered()){
+                String menuId = menuRenderer.renderMenu(context, table, column);
+                ajaxContext.addRenderedArea(menuId);
+                // }
+            }
+        }// for
+    }
+
+    public void contributorsEncodeHere(FacesContext context, UIDataTable table)
+            throws IOException {
+        RendererContributor[] contribs = composite.getContributors();
+
+        if (contribs != null) {
+            for (int i = 0; i < contribs.length; i++) {
+                RendererContributor rendererContributor = contribs[i];
+
+                if (rendererContributor instanceof HTMLEncodingContributor) {
+                    ((HTMLEncodingContributor) rendererContributor).encode(
+                            context, table);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/ExtendedTableHolder.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/ExtendedTableHolder.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/ExtendedTableHolder.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,89 @@
+/**
+ * 
+ */
+package org.richfaces.renderkit;
+
+import org.richfaces.component.UIColumn;
+import org.richfaces.component.UIExtendedDataTable;
+
+/**
+ * Extended table holder. It keeps additional information like last row key, last row data
+ * and information about grouping.
+ * @author pawelgo
+ *
+ */
+public class ExtendedTableHolder extends TableHolder {
+	
+	private Object lastData = null;
+	private Object lastKey = null;
+	//private int curRowId = -1;
+	private int groupRowCounter = -1;
+	
+	private boolean groupingOn = false;
+	private UIColumn groupingColumn = null;
+	private String groupingColumnLabel = null;
+	
+	/**
+	 * 
+	 * @param table
+	 */
+	public ExtendedTableHolder(UIExtendedDataTable table) {
+		super(table);
+		lastData = null;
+		lastKey = null;
+		groupRowCounter = 0;
+		groupingOn = table.isGroupingOn();
+		groupingColumnLabel = "";
+		if (groupingOn){
+			groupingColumn = table.getGroupingColumn();
+			if ((groupingColumn != null) && (groupingColumn instanceof UIColumn)){
+				groupingColumnLabel = ((UIColumn)groupingColumn).getColumnLabel();
+			}
+		}
+	}
+	
+	public UIExtendedDataTable getTable() {
+		return (UIExtendedDataTable)super.getTable();
+	}
+
+	public Object getLastData() {
+		return lastData;
+	}
+
+	public void setLastData(Object lastData) {
+		this.lastData = lastData;
+	}
+
+	public Object getLastKey() {
+		return lastKey;
+	}
+
+	public void setLastKey(Object lastKey) {
+		this.lastKey = lastKey;
+	}
+	
+	public int getGroupRowCounter() {
+		return groupRowCounter;
+	}
+
+	/**
+	 * Get current rendered row number, and increment to next value.
+	 * @return the rowCounter
+	 */
+	public int nextGroupRow() {
+		return groupRowCounter++;
+	}
+	
+	public String getGroupingColumnLabel() {
+		return groupingColumnLabel;
+	}
+
+	public boolean isGroupingOn() {
+		return groupingOn;
+	}
+
+	public UIColumn getGroupingColumn() {
+		return groupingColumn;
+	}
+
+}

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/DraggableRendererContributor.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/DraggableRendererContributor.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/DraggableRendererContributor.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,112 @@
+/**
+ * 
+ */
+package org.richfaces.renderkit.html;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+import org.richfaces.renderkit.CompositeRenderer;
+import org.richfaces.renderkit.RendererContributor;
+import org.richfaces.renderkit.ScriptOptions;
+
+/**
+ * Renderer contributor for drag support based on
+ * {@link org.richfaces.renderkit.DraggableRendererContributor}. Used only with
+ * {@link org.richfaces.component.UIExtendedDataTable} component. All
+ * functionality from wrapped
+ * {@link org.richfaces.renderkit.DraggableRendererContributor} are available
+ * and special method
+ * {@link DraggableRendererContributor#buildOptions(FacesContext, UIComponent, String, String)}
+ * is added.
+ * 
+ * @author pawelgo
+ * 
+ */
+public class DraggableRendererContributor implements RendererContributor {
+
+    private static DraggableRendererContributor instance;
+
+    private static RendererContributor wrappedContributor;
+
+    private DraggableRendererContributor() {
+        super();
+    }
+
+    public static synchronized DraggableRendererContributor getInstance() {
+        if (instance == null) {
+            instance = new DraggableRendererContributor();
+            wrappedContributor = org.richfaces.renderkit.DraggableRendererContributor
+                    .getInstance();
+        }
+        return instance;
+    }
+
+    public void decode(FacesContext context, UIComponent component,
+            CompositeRenderer compositeRenderer) {
+        wrappedContributor.decode(context, component, compositeRenderer);
+    }
+
+    public Class<?> getAcceptableClass() {
+        return wrappedContributor.getAcceptableClass();
+    }
+
+    public String getScriptContribution(FacesContext context,
+            UIComponent component) {
+        return wrappedContributor.getScriptContribution(context, component);
+    }
+
+    public String[] getScriptDependencies() {
+        return wrappedContributor.getScriptDependencies();
+    }
+
+    public String[] getStyleDependencies() {
+        return wrappedContributor.getStyleDependencies();
+    }
+
+    public ScriptOptions buildOptions(FacesContext context,
+            UIComponent component) {
+        return wrappedContributor.buildOptions(context, component);
+    }
+
+    /**
+     * Builds options for DnD.G3SimpleDraggable JavaScript object. These options
+     * are specialized for drag source used to start changing table columns
+     * order event.
+     * 
+     * @param context
+     *            faces context
+     * @param column
+     *            table column
+     * @param dragSourceScriptId
+     *            drag source HTML element id
+     * @param indicatorId
+     *            drag indicator id
+     * @return all options needed for drag JavaScript object to work
+     */
+    public ScriptOptions buildOptions(FacesContext context, UIComponent column,
+            String dragSourceScriptId, String indicatorId) {
+
+        ScriptOptions options = new ScriptOptions(column);
+        options.addOption("dragType", "COLUMN_ORDER_"
+                + column.getParent().getClientId(context));
+
+        Map<String, Object> parameters = new HashMap<String, Object>();
+        parameters
+                .put(
+                        org.richfaces.renderkit.DraggableRendererContributor.DRAG_SOURCE_ID,
+                        dragSourceScriptId);
+        parameters.put(dragSourceScriptId, dragSourceScriptId);
+        options.addOption("parameters", parameters);
+
+        if (indicatorId != null) {
+            options.addOption("dragIndicator", indicatorId);
+        }
+
+        return options;
+    }
+
+}

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/DropzoneRendererContributor.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/DropzoneRendererContributor.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/DropzoneRendererContributor.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,145 @@
+/**
+ * 
+ */
+package org.richfaces.renderkit.html;
+
+import java.util.Map;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+import org.ajax4jsf.javascript.JSFunction;
+import org.ajax4jsf.javascript.JSFunctionDefinition;
+import org.ajax4jsf.javascript.JSReference;
+import org.ajax4jsf.javascript.ScriptUtils;
+import org.ajax4jsf.renderkit.AjaxRendererUtils;
+import org.richfaces.renderkit.CompositeRenderer;
+import org.richfaces.renderkit.RendererContributor;
+import org.richfaces.renderkit.ScriptOptions;
+
+/**
+ * Renderer contributor for drop support based on {@link org.richfaces.renderkit.DropzoneRendererContributor}.
+ * Used only with {@link org.richfaces.component.UIExtendedDataTable} component.
+ * All functionality from wrapped {@link org.richfaces.renderkit.DropzoneRendererContributor} are available.
+ * Also special methods {@link DropzoneRendererContributor#buildOptions(FacesContext, UIComponent)}
+ * and {@link DropzoneRendererContributor#getScriptContribution(FacesContext, UIComponent, String, JSFunctionDefinition)}
+ * are added.
+ * 
+ * @author pawelgo
+ *
+ */
+public class DropzoneRendererContributor implements RendererContributor {
+	
+	private DropzoneRendererContributor() {
+		super();
+	}
+
+	private static DropzoneRendererContributor instance;
+	
+	private static RendererContributor wrappedContributor;
+
+	public static synchronized DropzoneRendererContributor getInstance() {
+		if (instance == null) {
+			instance = new DropzoneRendererContributor();
+			wrappedContributor = org.richfaces.renderkit.DropzoneRendererContributor.getInstance();
+		}
+
+		return instance;
+	}
+
+	public void decode(FacesContext context, UIComponent component, CompositeRenderer compositeRenderer) {
+		wrappedContributor.decode(context, component, compositeRenderer);
+	}
+
+	public Class<?> getAcceptableClass() {
+		return wrappedContributor.getAcceptableClass();
+	}
+
+	public String getScriptContribution(FacesContext context, UIComponent component) {
+		return wrappedContributor.getScriptContribution(context, component);
+	}
+	
+	/**
+	 * Build JavaScript code for drag'n drop support specialized for change table column order event.
+	 * @param context faces context
+	 * @param column table column
+	 * @param dropTargetScriptId id of HTML script element that contains defined DnD.G3SimpleDropZone object
+	 * @param onAjaxCompleteFunction JavaScript function to be called on AJAX request complete 
+	 * @return JavaScript code
+	 */
+	public String getScriptContribution(FacesContext context, UIComponent column, String dropTargetScriptId, JSFunction preSendAjaxRequestFunction, JSFunctionDefinition onAjaxCompleteFunction) {
+		StringBuffer result = new StringBuffer();
+
+		result.append(".drop = ");
+
+		JSFunctionDefinition definition = new JSFunctionDefinition();
+		definition.addParameter("event");
+		definition.addParameter("drag");
+
+		Map<String, Object> requestOpts = AjaxRendererUtils.buildEventOptions(context, column);
+		//replace parameters
+		String clientId = column.getClientId(context);
+		@SuppressWarnings("unchecked")
+		Map<String, Object> parameters = (Map<String, Object>) requestOpts.get("parameters");
+		if (parameters != null){
+			if (parameters.containsKey(clientId)){
+				parameters.remove(clientId);
+				parameters.put(dropTargetScriptId, dropTargetScriptId);
+			}
+		}
+		
+		if (preSendAjaxRequestFunction != null){
+			definition.addToBody(preSendAjaxRequestFunction.toScript()).addToBody(";");
+		}
+		definition.addToBody("var options = ").addToBody(ScriptUtils.toScript(requestOpts)).addToBody(";");
+		definition.addToBody("options.parameters['" + org.richfaces.renderkit.DropzoneRendererContributor.DROP_TARGET_ID + "'] = '" + dropTargetScriptId + "';");
+		
+		if (onAjaxCompleteFunction != null)		
+			definition.addToBody("options['" + AjaxRendererUtils.ONCOMPLETE_ATTR_NAME + "'] = " + onAjaxCompleteFunction.toScript() + ";");
+		//TODO nick - remove as legacy
+		definition.addToBody("Object.extend(options.parameters,drag.getParameters());");
+		definition.addToBody("var dzOptions = this.getDropzoneOptions(); if (dzOptions.ondrop) { if (!dzOptions.ondrop.call(this, event)) return; };");
+		
+		JSFunction dropFunction = AjaxRendererUtils.buildAjaxFunction(column, context);
+		dropFunction.addParameter(new JSReference("options"));
+		
+		definition.addToBody(dropFunction.toScript()).addToBody(";");
+		definition.appendScript(result);
+		result.append(";");
+
+		return result.toString();
+	}
+	
+	/**
+	 * Build JavaScript code for drag'n drop support specialized for change table column order event.
+	 * @param context faces context
+	 * @param column table column
+	 * @param dropTargetScriptId id of HTML script element that contains defined DnD.G3SimpleDropZone object
+	 * @return JavaScript code
+	 */
+	public String getScriptContribution(FacesContext context, UIComponent column, String dropTargetScriptId) {
+		return getScriptContribution(context, column, dropTargetScriptId, null, null);
+	}
+
+	public String[] getScriptDependencies() {
+		return wrappedContributor.getScriptDependencies();
+	}
+
+	public String[] getStyleDependencies() {
+		return wrappedContributor.getStyleDependencies();
+	}
+
+	/**
+	 * Builds options for DnD.G3SimpleDropZone JavaScript object.
+	 * These options are specialized for drop target used to end changing table columns order event.
+	 * @param context faces context
+	 * @param column table column
+	 * @return all options needed for drop JavaScript object to work
+	 */
+	public ScriptOptions buildOptions(FacesContext context, UIComponent column) {
+		ScriptOptions options = new ScriptOptions(column);
+		options.addOption("acceptedTypes", "COLUMN_ORDER_"+column.getParent().getClientId(context));
+		return options;
+	}
+
+}

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/RichTableMenuRenderer.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/RichTableMenuRenderer.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/RichTableMenuRenderer.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,390 @@
+/**
+ * 
+ */
+package org.richfaces.renderkit.html;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+import javax.faces.component.UIComponent;
+
+import org.ajax4jsf.Messages;
+import org.ajax4jsf.javascript.JSFunction;
+import org.ajax4jsf.javascript.JSFunctionDefinition;
+import org.ajax4jsf.javascript.JSReference;
+import org.richfaces.component.UIColumn;
+import org.richfaces.component.UIContextMenu;
+import org.richfaces.component.UIMenuGroup;
+import org.richfaces.component.UIMenuItem;
+import org.richfaces.component.UIMenuSeparator;
+import org.richfaces.component.html.ContextMenu;
+import org.richfaces.component.html.HtmlMenuGroup;
+import org.richfaces.component.html.HtmlMenuItem;
+import org.richfaces.messages.SandboxMessageUtil;
+
+/**
+ * Renderer class for table menu.
+ * 
+ * @author pawelgo
+ * 
+ */
+public class RichTableMenuRenderer extends TableMenuRenderer {
+
+    private static final long serialVersionUID = -6812995542681604002L;
+
+    /*
+     * Message key constants
+     */
+    private static final String MSG_COLUMNS = "org.richfaces.component.UIExtendedDataTable.Menu.Columns";
+    private static final String MSG_SORT_ASC = "org.richfaces.component.UIExtendedDataTable.Menu.SortAscending";
+    private static final String MSG_SORT_DESC = "org.richfaces.component.UIExtendedDataTable.Menu.SortDescending";
+    private static final String MSG_GROUP_ON = "org.richfaces.component.UIExtendedDataTable.Menu.GroupByColumn";
+    private static final String MSG_GROUP_OFF = "org.richfaces.component.UIExtendedDataTable.Menu.DisableGrouping";
+
+    private int visibleColumnsCount;
+
+    private UIContextMenu menu;
+
+    /*
+     * Icon URIs
+     */
+    private static String iconColumnsURI;
+    private static String iconSortAscURI;
+    private static String iconSortDescURI;
+    private static String iconGroupURI;
+    private static String iconCheckedURI;
+    private static String iconUncheckedURI;
+
+    /**
+     * Initializes resources if they are not initialized yet. Each icon URI is
+     * kept in class field to ensure that resource URI is build only once.
+     */
+    private void initResources() {
+        if (context == null) {
+            throw new NullPointerException(Messages.getMessage(
+                    Messages.CONTEXT_NULL_ERROR, "initResources"));
+        }
+
+        if (iconColumnsURI == null) {
+            iconColumnsURI = getUri(
+                    getResource("/org/richfaces/renderkit/html/images/columns.png"),
+                    context);
+        }
+        if (iconSortAscURI == null) {
+            iconSortAscURI = getUri(
+                    getResource("/org/richfaces/renderkit/html/images/menu-sort-asc.png"),
+                    context);
+        }
+        if (iconSortDescURI == null) {
+            iconSortDescURI = getUri(
+                    getResource("/org/richfaces/renderkit/html/images/menu-sort-desc.png"),
+                    context);
+        }
+        if (iconGroupURI == null) {
+            iconGroupURI = getUri(
+                    getResource("/org/richfaces/renderkit/html/images/group-by.png"),
+                    context);
+        }
+        if (iconCheckedURI == null) {
+            iconCheckedURI = getUri(
+                    getResource("/org/richfaces/renderkit/html/images/checked.gif"),
+                    context);
+        }
+        if (iconUncheckedURI == null) {
+            iconUncheckedURI = getUri(
+                    getResource("/org/richfaces/renderkit/html/images/unchecked.gif"),
+                    context);
+        }
+    }// initResources
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.richfaces.renderkit.html.TableMenuRenderer#render()
+     */
+    public String render() throws IOException {
+        initResources();
+        ensureMenuBuilt();
+        String menuId = menu.getClientId(context);
+        // render menu
+        menu.encodeAll(context);
+        return menuId;
+    }// render
+
+    /**
+     * 
+     * @throws IOException
+     */
+    protected void ensureMenuBuilt() throws IOException {
+        menu = null;
+        // try to get menu from children components
+        for (UIComponent comp : table.getChildren()) {
+            if ((comp.getId().equals(buildMenuId()))
+                    && (comp instanceof UIContextMenu)) {
+                menu = (UIContextMenu) comp;
+                break;
+            }
+        }
+        buildMenu();
+    }
+
+    /**
+     * Builds menu id base on column for which menu is built.
+     * 
+     * @return menu id
+     */
+    protected String buildMenuId() {
+        return column.getId() + "menu";
+    }
+
+    /**
+     * Creates menu and ands it to table as child.
+     * 
+     * @throws IOException
+     */
+    protected void createMenu() throws IOException {
+        menu = (UIContextMenu) context.getApplication().createComponent(
+                UIContextMenu.COMPONENT_TYPE);
+        menu.setId(buildMenuId());
+        menu.setAttached(false);
+        menu.setSubmitMode("none");
+        menu.setEvent("onclick");
+        menu.setDisableDefaultMenu(false);
+
+        if (menu instanceof ContextMenu) {
+            ((ContextMenu) menu)
+                    .setStyle("z-index: 100; text-align: left; font-weight: normal;");
+
+        }
+        table.getChildren().add(menu);
+    }
+
+    /**
+     * Builds menu for current column. Creates menu if not exist yet and adds
+     * items.
+     * 
+     * @throws IOException
+     */
+    protected void buildMenu() throws IOException {
+        if (menu == null) {// menu does not exist yet
+            // create menu
+            createMenu();
+        } else {// menu already exists
+            // clear menu children
+            menu.getChildren().clear();
+        }
+
+        // add menu items for sorting
+        buildSortMenuItem(menu, true);
+        buildSortMenuItem(menu, false);
+
+        UIMenuSeparator sep = (UIMenuSeparator) context.getApplication()
+                .createComponent(UIMenuSeparator.COMPONENT_TYPE);
+        menu.getChildren().add(sep);
+
+        // add menu item for grouping
+        buildGroupMenuItem(menu);
+
+        sep = (UIMenuSeparator) context.getApplication().createComponent(
+                UIMenuSeparator.COMPONENT_TYPE);
+        menu.getChildren().add(sep);
+
+        // add menu items for changing column visibility
+        UIMenuGroup group = (HtmlMenuGroup) context.getApplication()
+                .createComponent(UIMenuGroup.COMPONENT_TYPE);
+        group.setValue(SandboxMessageUtil.getMessage(context, MSG_COLUMNS,
+                new Object[] {}).getSummary());
+        group.setIcon(iconColumnsURI);
+        menu.getChildren().add(group);
+
+        Iterator<UIColumn> columns = table.getSortedColumns();
+        visibleColumnsCount = table.getVisibleColumnsCount();
+
+        while (columns.hasNext()) {
+            buildMenuItem(group, columns.next());
+        }// while
+    }// buildMenu
+
+    /**
+     * Builds menu item for sorting.
+     * 
+     * @param parent
+     *            parent component for created item
+     * @param asc
+     *            sort direction
+     * @throws IOException
+     */
+    protected void buildSortMenuItem(UIComponent parent, boolean asc)
+            throws IOException {
+        UIMenuItem menuItem = (UIMenuItem) context.getApplication()
+                .createComponent(UIMenuItem.COMPONENT_TYPE);
+
+        menuItem.setSubmitMode("none");
+        String actionScript = null;
+        StringBuilder actionScriptBuilder = new StringBuilder();
+        if ((Boolean) column.getAttributes().get("sortable")) {
+            if (sortFunction != null) {
+                if (prepareFunction != null) {
+                    actionScriptBuilder.append(prepareFunction.toScript())
+                            .append("; ");
+                }
+
+                actionScriptBuilder.append(sortFunction.toScript());
+                actionScript = actionScriptBuilder.toString();
+                if (actionScript.contains("{columnId}")) {
+                    String columnClientId = (String) column.getAttributes()
+                            .get("columnClientId");
+                    if (columnClientId == null)
+                        columnClientId = column.getClientId(context);
+                    actionScript = actionScript.replace("{columnId}",
+                            columnClientId);
+                }
+                if (actionScript.contains("{sortDirection}"))
+                    actionScript = actionScript.replace("{sortDirection}",
+                            asc ? "asc" : "desc");
+            }
+        } else {
+            menuItem.setDisabled(true);
+        }
+        menuItem.setValue(SandboxMessageUtil.getMessage(context,
+                (asc ? MSG_SORT_ASC : MSG_SORT_DESC), new Object[] {})
+                .getSummary());
+        menuItem.setIcon((asc ? iconSortAscURI : iconSortDescURI));
+        if (menuItem instanceof HtmlMenuItem) {
+            ((HtmlMenuItem) menuItem)
+                    .setOnclick(actionScript == null ? "return false;"
+                            : actionScript);
+        }
+
+        // add item to parent
+        parent.getChildren().add(menuItem);
+    }// buildSortMenuItem
+
+    /**
+     * Builds menu item for sorting.
+     * 
+     * @param parent
+     *            parent component for created item
+     * @throws IOException
+     */
+    protected void buildGroupMenuItem(UIComponent parent) throws IOException {
+        UIMenuItem menuItem = (UIMenuItem) context.getApplication()
+                .createComponent(UIMenuItem.COMPONENT_TYPE);
+
+        menuItem.setSubmitMode("none");
+        String actionScript = null;
+        StringBuilder actionScriptBuilder = new StringBuilder();
+        boolean isGroupingColumn = column.getId().equalsIgnoreCase(
+                table.getGroupingColumnId());
+        if ((Boolean) column.getAttributes().get("sortable")) {
+            if (sortFunction != null) {
+                if (prepareFunction != null) {
+                    actionScriptBuilder.append(prepareFunction.toScript())
+                            .append("; ");
+                }
+                actionScriptBuilder.append(groupFunction.toScript());
+                actionScript = actionScriptBuilder.toString();
+                if (actionScript.contains("{columnId}")) {
+                    String columnClientId = (String) column.getAttributes()
+                            .get("columnClientId");
+                    if (columnClientId == null)
+                        columnClientId = column.getClientId(context);
+                    if (isGroupingColumn) {
+                        columnClientId = "";
+                    }
+                    actionScript = actionScript.replace("{columnId}",
+                            columnClientId);
+                }
+            }
+        } else {
+            menuItem.setDisabled(true);
+        }
+
+        menuItem.setValue(SandboxMessageUtil.getMessage(context,
+                (isGroupingColumn ? MSG_GROUP_OFF : MSG_GROUP_ON),
+                new Object[] {}).getSummary());
+        menuItem.setIcon(iconGroupURI);
+        if (menuItem instanceof HtmlMenuItem) {
+            ((HtmlMenuItem) menuItem)
+                    .setOnclick(actionScript == null ? "return false;"
+                            : actionScript);
+        }
+
+        // add item to menu
+        parent.getChildren().add(menuItem);
+    }// buildSortMenuItem
+
+    /**
+     * Builds menu items for changing column visibility.
+     * 
+     * @param parent
+     *            parent component for created item
+     * @param col
+     *            column component correspond to created item
+     * @throws IOException
+     */
+    protected void buildMenuItem(UIComponent parent, UIColumn col)
+            throws IOException {
+        if (col instanceof UIColumn) {
+            UIColumn dataColumn = (UIColumn) col;
+            UIMenuItem menuItem = (UIMenuItem) context.getApplication()
+                    .createComponent(UIMenuItem.COMPONENT_TYPE);
+
+            menuItem.setSubmitMode("none");
+            Boolean v = dataColumn.isVisible();
+            boolean columnVisible = (v == null ? Boolean.TRUE : v);
+            String actionScript = null;
+            StringBuilder actionScriptBuilder = new StringBuilder();
+
+            menuItem.setStyle("text-align: left;");
+
+            if ((!columnVisible) || (visibleColumnsCount > 1)) {
+                if (changeColumnVisibilityFunction != null) {
+                    if (prepareFunction != null) {
+                        actionScriptBuilder.append(prepareFunction.toScript())
+                                .append("; ");
+                    }
+                    actionScriptBuilder.append(changeColumnVisibilityFunction
+                            .toScript());
+                    actionScript = actionScriptBuilder.toString();
+                    if (actionScript.contains("{columnId}"))
+                        actionScript = actionScript.replace("{columnId}",
+                                dataColumn.getId());
+                }
+            }// if
+            menuItem.setValue(dataColumn.getColumnLabel());
+            menuItem.setIcon(columnVisible ? iconCheckedURI : iconUncheckedURI);
+            if (menuItem instanceof HtmlMenuItem) {
+                ((HtmlMenuItem) menuItem)
+                        .setOnclick(actionScript == null ? "return false;"
+                                : actionScript);
+            }
+
+            // add item to menu
+            parent.getChildren().add(menuItem);
+        }
+    }// buildMenuItem
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.richfaces.renderkit.html.TableMenuRenderer#createShowMenuEventFunction()
+     */
+    public JSFunctionDefinition createShowMenuEventFunction() {
+        JSFunctionDefinition definition = new JSFunctionDefinition();
+        definition.addParameter("event");
+        definition.addParameter("columnId");
+        definition.addParameter("menuId");
+        JSFunction invocation = new JSFunction(
+                "Richfaces.componentControl.performOperation");
+        invocation.addParameter(new JSReference("event"));
+        invocation.addParameter(new JSReference("menuId"));
+        invocation.addParameter("show");
+        // invocation.addParameter(new JSReference("{'columnId':columnId}"));
+        invocation.addParameter(new JSReference("{}"));
+        invocation.addParameter(Boolean.FALSE);
+        definition.addToBody(invocation.toScript()).addToBody(";\n");
+        return definition;
+    }
+
+}

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/SimpleTableMenuRenderer.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/SimpleTableMenuRenderer.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/SimpleTableMenuRenderer.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,132 @@
+/**
+ * 
+ */
+package org.richfaces.renderkit.html;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.faces.context.ResponseWriter;
+
+import org.ajax4jsf.javascript.JSFunction;
+import org.ajax4jsf.javascript.JSFunctionDefinition;
+import org.ajax4jsf.javascript.JSReference;
+import org.ajax4jsf.renderkit.AjaxRendererUtils;
+import org.ajax4jsf.renderkit.RendererUtils.HTML;
+import org.richfaces.component.UIColumn;
+
+/**
+ * @author pawelgo
+ * 
+ */
+public class SimpleTableMenuRenderer extends TableMenuRenderer {
+
+    private static final long serialVersionUID = -3907452284006250197L;
+
+    private int visibleColumnsCount;
+    private ResponseWriter writer;
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.richfaces.renderkit.html.TableMenuRenderer#render()
+     */
+    public String render() throws IOException {
+
+        String menuId = table.getClientId(context) + ":tm";
+
+        Iterator<UIColumn> columns = table.getSortedColumns();
+        visibleColumnsCount = table.getVisibleColumnsCount();
+
+        writer = context.getResponseWriter();
+        // print main menu DIV element
+        writer.startElement(HTML.DIV_ELEM, table);
+
+        writer.writeAttribute(HTML.id_ATTRIBUTE, menuId, null);
+        writer.writeAttribute(HTML.class_ATTRIBUTE, "dt-menu", null);
+        writer.writeAttribute(HTML.style_ATTRIBUTE,
+                "z-index: 15005; visibility: visible; left: 0px; top: 0px;",
+                null);
+        // writer.writeAttribute(HTML.style_ATTRIBUTE, "position: absolute;
+        // z-index: 15005; visibility: visible; left: 0px; top: 0px;", null);
+        writer.startElement("ul", table);
+        writer.writeAttribute(HTML.class_ATTRIBUTE, "dt-menu-list", null);
+        for (; columns.hasNext();) {
+            renderMenuItem(columns.next());
+        }// for
+        writer.endElement("ul");
+        writer.endElement(HTML.DIV_ELEM);
+        return menuId;
+    }// render
+
+    protected void renderMenuItem(UIColumn column) throws IOException {
+        if (column instanceof UIColumn) {
+            UIColumn dataColumn = (UIColumn) column;
+            Boolean v = dataColumn.isVisible();
+            boolean columnVisible = (v == null ? Boolean.TRUE : v);
+            String actionScript = null;
+            if ((!columnVisible) || (visibleColumnsCount > 1)) {
+                boolean ajaxSingle = true;
+                Map<String, Object> requestOpts = AjaxRendererUtils
+                        .buildEventOptions(context, dataColumn);
+
+                if (onAjaxCompleteFunction != null)
+                    requestOpts.put(AjaxRendererUtils.ONCOMPLETE_ATTR_NAME,
+                            onAjaxCompleteFunction);
+
+                @SuppressWarnings("unchecked")
+                Map<String, Object> parameters = (Map<String, Object>) requestOpts
+                        .get("parameters");
+                if (parameters == null) {
+                    parameters = new HashMap<String, Object>();
+                    requestOpts.put("parameters", parameters);
+                }
+                if (ajaxSingle) {
+                    if (!parameters
+                            .containsKey(AjaxRendererUtils.AJAX_SINGLE_PARAMETER_NAME))
+                        parameters.put(
+                                AjaxRendererUtils.AJAX_SINGLE_PARAMETER_NAME,
+                                dataColumn.getParent().getClientId(context));
+                    if (!requestOpts.containsKey("control"))
+                        requestOpts.put("control", JSReference.THIS);
+                }
+                parameters.put(dataColumn.getParent().getClientId(context)
+                        + ":" + CHANGE_COL_VISIBILITY, dataColumn.getId());
+
+                JSFunction dropFunction = AjaxRendererUtils.buildAjaxFunction(
+                        dataColumn, context);
+                dropFunction.addParameter(requestOpts);
+                actionScript = dropFunction.toScript();
+            }// if
+
+            writer.startElement("li", dataColumn);
+            writer.writeAttribute(HTML.class_ATTRIBUTE, "dt-menu-list-item"
+                    + (columnVisible ? " dt-menu-item-checked" : ""), null);
+
+            writer.startElement(HTML.a_ELEMENT, dataColumn);
+            writer.writeAttribute(HTML.HREF_ATTR, "#", null);
+            writer.writeAttribute(HTML.class_ATTRIBUTE,
+                    "dt-menu-item dt-menu-check-item", null);
+            actionScript = (actionScript == null ? "return false;"
+                    : actionScript + ";return false;");
+            writer.writeAttribute(HTML.onclick_ATTRIBUTE, actionScript, null);
+            writer.startElement(HTML.IMG_ELEMENT, dataColumn);
+            writer.writeAttribute(HTML.class_ATTRIBUTE, "dt-menu-item-icon",
+                    null);
+            writer.writeAttribute(HTML.src_ATTRIBUTE, getResource(
+                    "/org/richfaces/renderkit/html/images/s.gif").getUri(
+                    context, null), null);
+            writer.endElement(HTML.IMG_ELEMENT);
+            writer.writeText(dataColumn.getColumnLabel(), null);
+            writer.endElement(HTML.a_ELEMENT);
+            writer.endElement("li");
+        }
+    }// encodeMenuItem
+
+    public JSFunctionDefinition createShowMenuEventFunction() {
+        return new JSFunctionDefinition();
+    }
+
+}

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/TableDragDropRenderer.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/TableDragDropRenderer.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/TableDragDropRenderer.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,238 @@
+/**
+ * 
+ */
+package org.richfaces.renderkit.html;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+
+import org.ajax4jsf.javascript.JSFunction;
+import org.ajax4jsf.javascript.JSFunctionDefinition;
+import org.ajax4jsf.org.w3c.tidy.EntityTable;
+import org.ajax4jsf.renderkit.RendererUtils.HTML;
+import org.richfaces.component.UIColumn;
+import org.richfaces.json.JSONException;
+import org.richfaces.json.JSONObject;
+import org.richfaces.renderkit.DragIndicatorRendererBase;
+import org.richfaces.renderkit.ScriptOptions;
+
+/**
+ * Renderer for support drag'n drop for
+ * {@link org.richfaces.component.UIExtendedDataTable} component specialized for
+ * changing table columns order event.
+ * 
+ * @author pawelgo
+ * 
+ */
+public class TableDragDropRenderer implements Serializable {
+
+    private static final long serialVersionUID = -8059307641808179967L;
+
+    public final static String DRAG_SOURCE_SCRIPT_ID = "dnd_drag_script";
+
+    public final static String DROP_TARGET_SCRIPT_ID = "dnd_drop_script";
+
+    public final static String DROP_TARGET_BEFORE = "_left";
+
+    public final static String DROP_TARGET_AFTER = "_right";
+
+    private FacesContext context;
+
+    private JSFunctionDefinition onAjaxCompleteFunctionDef;
+
+    private JSFunction preSendAjaxRequestFunction;
+
+    private TableDragDropRenderer(FacesContext context) {
+        super();
+        this.context = context;
+    }
+
+    /**
+     * Creates new instance.
+     * 
+     * @param context
+     *            faces context
+     * @return instance object
+     */
+    public static TableDragDropRenderer getInstance(FacesContext context) {
+        return new TableDragDropRenderer(context);
+    }
+
+    /**
+     * Encodes child scripts. It uses
+     * {@link org.richfaces.renderkit.DragIndicatorRendererBase} renderer and
+     * overwrites marker for DEFAULT drag indicator state. Drag indicator image
+     * is the same in DEFAULT and REJECT states.
+     * 
+     * @param context
+     *            faces context
+     * @param component
+     *            table component
+     * @throws IOException
+     */
+    public void encodeChildScripts(FacesContext context, UIComponent component)
+            throws IOException {
+        // new DragIndicatorRendererBase().encodeChildScripts(context,
+        // component);
+        new DragIndicatorRendererBase() {
+            public void encodeChildScripts(FacesContext context,
+                    UIComponent component) throws IOException {
+                super.encodeChildScripts(context, component);
+                // redefine DEFAULT marker to be like REJECT marker
+                ResponseWriter responseWriter = context.getResponseWriter();
+                responseWriter.write("elt.markers['" + DEFAULT + "'] = \"");
+                responseWriter.write(getPredefinedMarker(context, REJECT));
+                responseWriter.write("\";\n");
+            }
+        }.encodeChildScripts(context, component);
+    }
+
+    /**
+     * Renders support for drag operation specialized for changing table columns
+     * order event.
+     * 
+     * @param column
+     *            table column
+     * @param dragSourceId
+     *            drag source HTML element id
+     * @param indicatorId
+     *            drag indicator id
+     * @param dragLabel
+     *            label to be displayed in indicator
+     * @throws IOException
+     */
+    public void renderDragSupport(UIColumn column, String dragSourceId,
+            String indicatorId, String dragLabel) throws IOException {
+        StringBuffer buffer = new StringBuffer();
+        JSFunction function = new JSFunction("new DnD.G3SimpleDraggable");
+        function.addParameter(dragSourceId);
+        String dragSourceScriptId = column.getClientId(context) + ":"
+                + DRAG_SOURCE_SCRIPT_ID;
+        DraggableRendererContributor contributor = DraggableRendererContributor
+                .getInstance();
+        ScriptOptions dragOptions = contributor.buildOptions(context, column,
+                dragSourceScriptId, indicatorId);
+
+        JSONObject dndParams = new JSONObject();
+        try {
+            dndParams.put("label", dragLabel == null ? "" : dragLabel);
+        } catch (JSONException e) {
+        }
+        dragOptions.addOption("dndParams", dndParams.toString());
+
+        function.addParameter(dragOptions);
+        function.appendScript(buffer);
+
+        String scriptContribution = contributor.getScriptContribution(context,
+                column);
+        if (scriptContribution != null && scriptContribution.length() != 0) {
+            buffer.append(scriptContribution);
+        }
+
+        ResponseWriter writer = context.getResponseWriter();
+
+        writer.startElement(HTML.SCRIPT_ELEM, column);
+        writer.writeAttribute("id", dragSourceScriptId, "id");
+        writer.write(escapeHtmlEntities(buffer));
+        writer.endElement(HTML.SCRIPT_ELEM);
+    }// renderDragSupport
+
+    /**
+     * Renders support for drop operation specialized for changing table columns
+     * order event.
+     * 
+     * @param column
+     *            table column
+     * @param dropTargetId
+     *            drop target HTML element id
+     * @param before
+     *            true if target is positioned before column
+     * @throws IOException
+     */
+    public void renderDropSupport(UIColumn column, String dropTargetId,
+            boolean before) throws IOException {
+        // RendererContributor contributor =
+        // DropzoneRendererContributor.getInstance();
+        DropzoneRendererContributor contributor = DropzoneRendererContributor
+                .getInstance();
+        StringBuffer buffer = new StringBuffer();
+        JSFunction function = new JSFunction("new DnD.G3SimpleDropZone");
+        function.addParameter(dropTargetId);
+        ScriptOptions dropOptions = contributor.buildOptions(context, column);
+        JSONObject dndParams = new JSONObject();
+        dropOptions.addOption("dndParams", dndParams.toString());
+
+        function.addParameter(dropOptions);
+        function.appendScript(buffer);
+
+        String dropTargetScriptId = column.getClientId(context) + ":"
+                + DROP_TARGET_SCRIPT_ID
+                + (before ? DROP_TARGET_BEFORE : DROP_TARGET_AFTER);
+        String scriptContribution = contributor.getScriptContribution(context,
+                column, dropTargetScriptId, preSendAjaxRequestFunction,
+                onAjaxCompleteFunctionDef);
+        if (scriptContribution != null && scriptContribution.length() != 0) {
+            buffer.append(scriptContribution);
+        }
+
+        ResponseWriter writer = context.getResponseWriter();
+
+        writer.startElement(HTML.SCRIPT_ELEM, column);
+        writer.writeAttribute("id", dropTargetScriptId, "id");
+        writer.write(escapeHtmlEntities(buffer));
+        writer.endElement(HTML.SCRIPT_ELEM);
+    }// renderDropSupport
+
+    /**
+     * Help method for escaping HTML entities.
+     * 
+     * @param orig
+     *            string to escape
+     * @return string with escaped HTML entities
+     */
+    protected String escapeHtmlEntities(CharSequence orig) {
+        StringBuffer buff = new StringBuffer(orig);
+        EntityTable defaultEntityTable = EntityTable.getDefaultEntityTable();
+        Matcher matcher = Pattern.compile("\\&\\w+\\;").matcher(orig);
+        int delta = 0;
+        while (matcher.find()) {
+            String name = matcher.group().substring(0,
+                    matcher.group().length() - 1);
+            int code = defaultEntityTable.entityCode(name);
+            if (0 != code) {
+                String replacement = "&#" + code + ";";
+                buff.replace(matcher.start() - delta, matcher.end() - delta,
+                        replacement);
+                delta = delta + matcher.group().length() - replacement.length();
+            }
+        }
+        return buff.toString();
+    }
+
+    public JSFunctionDefinition getOnAjaxCompleteFunctionDef() {
+        return onAjaxCompleteFunctionDef;
+    }
+
+    /**
+     * Set JavaScript function to be called on AJAX request complete.
+     * 
+     * @param onAjaxCompleteFunction
+     *            JavaScriot function to set
+     */
+    public void setOnAjaxCompleteFunctionDef(
+            JSFunctionDefinition onAjaxCompleteFunctionDef) {
+        this.onAjaxCompleteFunctionDef = onAjaxCompleteFunctionDef;
+    }
+
+    public void setPreSendAjaxRequestFunction(
+            JSFunction preSendAjaxRequestFunction) {
+        this.preSendAjaxRequestFunction = preSendAjaxRequestFunction;
+    }
+
+}

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/TableMenuRenderer.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/TableMenuRenderer.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/TableMenuRenderer.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,177 @@
+/**
+ * 
+ */
+package org.richfaces.renderkit.html;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+import javax.faces.FacesException;
+import javax.faces.context.FacesContext;
+
+import org.ajax4jsf.javascript.JSFunction;
+import org.ajax4jsf.javascript.JSFunctionDefinition;
+import org.ajax4jsf.resource.InternetResource;
+import org.ajax4jsf.resource.InternetResourceBuilder;
+import org.ajax4jsf.resource.ResourceNotFoundException;
+import org.ajax4jsf.webapp.WebXml;
+import org.richfaces.component.UIColumn;
+import org.richfaces.component.UIExtendedDataTable;
+
+/**
+ * Abstract renderer class for table menu.
+ * 
+ * @author pawelgo
+ * 
+ */
+public abstract class TableMenuRenderer implements Serializable {
+
+    public final static String CHANGE_COL_VISIBILITY = "change_col_v";
+
+    protected FacesContext context;
+    protected UIExtendedDataTable table;
+    protected UIColumn column;
+
+    protected JSFunction changeColumnVisibilityFunction;
+    protected JSFunction sortFunction;
+    protected JSFunction groupFunction;
+    protected JSFunction prepareFunction;
+    protected JSFunctionDefinition onAjaxCompleteFunction;
+
+    protected static InternetResourceBuilder resourceBuilder;
+
+    /**
+     * Performs initial operations and renders table menu.
+     * 
+     * @param context
+     *            faces context
+     * @param table
+     *            table component
+     * @param column
+     *            current column
+     * @return created menu (DOM element) id
+     * @throws IOException
+     */
+    public String renderMenu(FacesContext context, UIExtendedDataTable table,
+            UIColumn column) throws IOException {
+        this.context = context;
+        this.table = table;
+        this.column = column;
+        return render();
+    }
+
+    /**
+     * Renders table menu.
+     * 
+     * @return created menu (DOM element) id
+     * @throws IOException
+     */
+    protected abstract String render() throws IOException;
+
+    /**
+     * Creates JavaScript code to be executed in order to show menu on some
+     * event.
+     * 
+     * @return JavaScript code causes showing menu
+     */
+    public abstract JSFunctionDefinition createShowMenuEventFunction();
+
+    /**
+     * Base stub method for produce Internet resource ( image, script ... )
+     * since resources must be implemented in "lightweight" pattern, it
+     * instances put in internal map to caching.
+     * 
+     * @param resourceURI -
+     *            relative ( to renderer class ) URI to resource in jar or key
+     *            for generate ( in Java2D , for example ).
+     * @return - resource instance for this URI.
+     * @throws ResourceNotFoundException -
+     *             if requested resource not instantiated.
+     */
+    public InternetResource getResource(String resourceURI)
+            throws FacesException {
+        return getResourceBuilder().createResource(null, resourceURI);
+    }
+
+    protected static InternetResourceBuilder getResourceBuilder() {
+        if (resourceBuilder == null) {
+            resourceBuilder = InternetResourceBuilder.getInstance();
+        }
+        return resourceBuilder;
+    }
+
+    protected String getUri(InternetResource resource, FacesContext context) {
+        return getFacesResourceURL(context, resource.getKey());
+    }// getUri
+
+    protected String getFacesResourceURL(FacesContext context, String Url) {
+        WebXml webXml = WebXml.getInstance(context);
+        StringBuffer buf = new StringBuffer();
+        buf.append(webXml.getResourcePrefix()).append(Url);
+        // Insert suffix mapping
+        if (webXml.isPrefixMapping()) {
+            buf.insert(0, webXml.getFacesFilterPrefix());
+        } else {
+            int index;
+            if ((index = buf.indexOf("?")) >= 0) {
+                buf.insert(index, webXml.getFacesFilterSuffix());
+            } else {
+                buf.append(webXml.getFacesFilterSuffix());
+            }
+        }
+        return buf.toString();
+    }// getFacesResourceURL
+
+    /**
+     * Sets function to be called on complete AJAX request fired by menu action
+     * 
+     * @param functionDefinition
+     *            function to be called on complete AJAX request
+     */
+    public void setOnAjaxCompleteFunction(
+            JSFunctionDefinition functionDefinition) {
+        this.onAjaxCompleteFunction = functionDefinition;
+    }
+
+    /**
+     * Set JavaScript function for change column visibility.
+     * 
+     * @param changeColumnVisibilityFunction
+     *            JavaScript function
+     */
+    public void setChangeColumnVisibilityFunction(
+            JSFunction changeColumnVisibilityFunction) {
+        this.changeColumnVisibilityFunction = changeColumnVisibilityFunction;
+    }
+
+    /**
+     * Set JavaScript function for sort table.
+     * 
+     * @param sortFunction
+     *            JavaScript function
+     */
+    public void setSortFunction(JSFunction sortFunction) {
+        this.sortFunction = sortFunction;
+    }
+
+    /**
+     * Set JavaScript function for group table.
+     * 
+     * @param groupFunction
+     *            JavaScript function
+     */
+    public void setGroupFunction(JSFunction groupFunction) {
+        this.groupFunction = groupFunction;
+    }
+
+    /**
+     * Set JavaScript function called before send AJAX request.
+     * 
+     * @param prepareFunction
+     *            JavaScript function
+     */
+    public void setPrepareFunction(JSFunction prepareFunction) {
+        this.prepareFunction = prepareFunction;
+    }
+
+}

Added: trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/TableSelectionRendererContributor.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/TableSelectionRendererContributor.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/java/org/richfaces/renderkit/html/TableSelectionRendererContributor.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,325 @@
+/**
+ * 
+ */
+package org.richfaces.renderkit.html;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.el.ValueExpression;
+import javax.faces.FacesException;
+import javax.faces.application.Application;
+import javax.faces.component.UIComponent;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+import javax.faces.convert.Converter;
+
+import org.ajax4jsf.context.AjaxContext;
+import org.ajax4jsf.javascript.JSFunctionDefinition;
+import org.ajax4jsf.model.DataVisitor;
+import org.ajax4jsf.renderkit.RendererUtils.HTML;
+import org.richfaces.model.selection.ClientSelection;
+import org.richfaces.model.selection.Selection;
+import org.richfaces.model.selection.SimpleSelection;
+import org.richfaces.renderkit.CompositeRenderer;
+import org.richfaces.renderkit.RendererContributor;
+import org.richfaces.renderkit.ScriptOptions;
+import org.richfaces.renderkit.TableHolder;
+import org.richfaces.renderkit.html.HTMLEncodingContributor;
+
+import org.richfaces.component.UIExtendedDataTable;
+
+/**
+ * Renderer contributor based on
+ * {@link org.richfaces.renderkit.html.SelectionRendererContributor}} adapted
+ * for {@link org.richfaces.component.UIExtendedDataTable} component.
+ * 
+ * @author pawelgo
+ * 
+ */
+public class TableSelectionRendererContributor implements RendererContributor,
+        HTMLEncodingContributor {
+
+    public static final String CLIENT_SELECTION = "clientSelection";
+
+    public static final String getSelectionInputName(FacesContext context,
+            UIExtendedDataTable table) {
+        String id = table.getBaseClientId(context) + ":s";
+        return id;
+    }
+
+    public static final String getGridId(FacesContext context,
+            UIExtendedDataTable table) {
+        return table.getBaseClientId(context);
+    }
+
+    public void decode(FacesContext context, UIComponent component,
+            CompositeRenderer compositeRenderer) {
+
+        final UIExtendedDataTable table = (UIExtendedDataTable) component;
+
+        ExternalContext externalContext = context.getExternalContext();
+        Map<String, String> requestParamMap = externalContext
+                .getRequestParameterMap();
+        Application application = context.getApplication();
+
+        String id = getSelectionInputName(context, table);
+
+        String value = (String) requestParamMap.get(id);
+        if (value != null) {
+
+            Converter converter = application
+                    .createConverter(ClientSelection.class);
+
+            ClientSelection _oldClientSelection = (ClientSelection) table
+                    .getAttributes().get(CLIENT_SELECTION);
+
+            final ClientSelection oldClientSelection = _oldClientSelection == null ? new ClientSelection()
+                    : _oldClientSelection;
+
+            final ClientSelection clientSelection = (ClientSelection) converter
+                    .getAsObject(context, table, value);
+
+            // final ScrollableDataTableRendererState state =
+            // ScrollableDataTableRendererState.createState(context, grid);
+            // state.setRowIndex(ScrollableDataTableUtils.getClientRowIndex(grid));
+
+            final TableHolder holder = new TableHolder(table);
+
+            final SimpleSelection simpleSelection = table.getSelection() == null ? new SimpleSelection()
+                    : (SimpleSelection) table.getSelection();
+
+            if (clientSelection.isReset() || clientSelection.isSelectAll()) {
+                simpleSelection.clear();
+            }
+
+            try {
+                table.walk(context, new DataVisitor() {
+                    public void process(FacesContext context, Object rowKey,
+                            Object argument) throws IOException {
+
+                        // TableHolder holder = (TableHolder) argument;
+                        // int i = state.getRowIndex();
+                        int i = holder.getRowCounter();
+
+                        if (shouldAddToSelection(i, oldClientSelection,
+                                clientSelection)) {
+
+                            simpleSelection.addKey(rowKey);
+
+                        } else if (shouldRemoveFromSelection(i,
+                                oldClientSelection, clientSelection)) {
+
+                            simpleSelection.removeKey(rowKey);
+
+                        }
+
+                        if (i == clientSelection.getActiveRowIndex()) {
+                            table.setActiveRowKey(rowKey);
+                        }
+                        holder.nextRow();
+
+                    }
+                }, holder);
+            } catch (IOException e) {
+                throw new FacesException(e);
+            }
+
+            table.setSelection(simpleSelection);
+
+            ValueExpression selectionBinding = table
+                    .getValueExpression("selection");
+            if (selectionBinding != null) {
+                selectionBinding.setValue(context.getELContext(),
+                        simpleSelection);
+            }
+        }
+
+        // ScrollableDataTableRendererState.restoreState(context);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.richfaces.renderkit.RendererContributor#getAcceptableClass()
+     */
+    public Class<?> getAcceptableClass() {
+        return UIExtendedDataTable.class;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.richfaces.renderkit.RendererContributor#getScriptContribution(javax.faces.context.FacesContext,
+     *      javax.faces.component.UIComponent)
+     */
+    public String getScriptContribution(FacesContext context,
+            UIComponent component) {
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.richfaces.renderkit.RendererContributor#getScriptDependencies()
+     */
+    public String[] getScriptDependencies() {
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.richfaces.renderkit.RendererContributor#getStyleDependencies()
+     */
+    public String[] getStyleDependencies() {
+        return null;
+    }
+
+    public ScriptOptions buildOptions(FacesContext context,
+            UIComponent component) {
+        ScriptOptions scriptOptions = new ScriptOptions(component);
+        scriptOptions.addOption("selectionInput", getSelectionInputName(
+                context, (UIExtendedDataTable) component));
+        scriptOptions.addOption("gridId", getGridId(context,
+                (UIExtendedDataTable) component));
+        Map<String, Object> attributes = component.getAttributes();
+        Object attribut = attributes.get("selectedClass");
+        if (attribut == null) {
+            attribut = "";
+        }
+        scriptOptions.addOption("selectedClass", attribut);
+        attribut = attributes.get("activeClass");
+        if (attribut == null) {
+            attribut = "";
+        }
+        scriptOptions.addOption("activeClass", attribut);
+
+        attribut = attributes.get("selectionMode");
+        if (attribut == null) {
+            attribut = "";
+        }
+        scriptOptions.addOption("selectionMode", attribut);
+
+        attribut = attributes.get("onselectionchange");
+        if (attribut == null) {
+            attribut = "";
+        }
+        JSFunctionDefinition function = new JSFunctionDefinition();
+        function.addToBody(attribut);
+        scriptOptions.addEventHandler("onselectionchange", function);
+        return scriptOptions;
+    }
+
+    public void encode(FacesContext context, UIComponent component)
+            throws IOException {
+        UIExtendedDataTable grid = (UIExtendedDataTable) component;
+        encodeSelection(context, grid);
+        writeSelection(context, grid);
+    }
+
+    // Decide whether to add new row to selection based on comparison with old
+    // one
+    public boolean shouldAddToSelection(int i, ClientSelection oldSelection,
+            ClientSelection newSelection) {
+
+        return newSelection.isSelectAll()
+                || (newSelection.isSelected(i) && (!oldSelection.isSelected(i) || newSelection
+                        .isReset()));
+    }
+
+    // Decide whether to remove new row to selection based on comparison with
+    // old one
+    public boolean shouldRemoveFromSelection(int i,
+            ClientSelection oldSelection, ClientSelection newSelection) {
+        return !newSelection.isReset()
+                && (!newSelection.isSelectAll() && (!newSelection.isSelected(i) && oldSelection
+                        .isSelected(i)));
+    }
+
+    private void encodeSelection(FacesContext context,
+            final UIExtendedDataTable table) throws IOException {
+
+        // final ScrollableDataTableRendererState state =
+        // ScrollableDataTableRendererState.createState(context, grid);
+        // state.setRowIndex(ScrollableDataTableUtils.getClientRowIndex(grid));
+
+        final TableHolder holder = new TableHolder(table);
+
+        final Selection gridSelection = table.getSelection() == null ? new SimpleSelection()
+                : table.getSelection();
+        final ClientSelection clientSelection = new ClientSelection();
+
+        table.walk(context, new DataVisitor() {
+            public void process(FacesContext context, Object rowKey,
+                    Object argument) throws IOException {
+
+                // TableHolder holder = (TableHolder) argument;
+
+                if (gridSelection.isSelected(rowKey)) {
+
+                    int i = holder.getRowCounter();
+
+                    clientSelection.addIndex(i);
+                }
+
+                if (rowKey.equals(table.getActiveRowKey())) {
+                    clientSelection.setActiveRowIndex(holder.getRowCounter());
+                }
+
+                holder.nextRow();
+
+            }
+        }, holder);
+
+        // ScrollableDataTableRendererState.restoreState(context);
+        table.getAttributes().put(CLIENT_SELECTION, clientSelection);
+    }
+
+    /**
+     * Get client selection from the component, transform it into string form,
+     * and write it as hidden input
+     * 
+     * @param context
+     *            faces context
+     * @param table
+     *            table component
+     * @throws IOException
+     */
+    public void writeSelection(FacesContext context, UIExtendedDataTable table)
+            throws IOException {
+
+        Application application = context.getApplication();
+
+        Converter converter = application
+                .createConverter(ClientSelection.class);
+
+        ClientSelection selection = (ClientSelection) table.getAttributes()
+                .get(CLIENT_SELECTION);
+        String string = converter.getAsString(context, table, selection);
+
+        if (string == null) {
+            string = "";
+        }
+
+        string += selection.getActiveRowIndex();
+
+        String id = getSelectionInputName(context, table);
+
+        ResponseWriter writer = context.getResponseWriter();
+        writer.startElement(HTML.INPUT_ELEM, table);
+        writer.writeAttribute(HTML.TYPE_ATTR, "hidden", null);
+        writer.writeAttribute(HTML.id_ATTRIBUTE, id, null);
+        writer.writeAttribute(HTML.NAME_ATTRIBUTE, id, null);
+        writer.writeAttribute(HTML.value_ATTRIBUTE, string, null);
+        writer.endElement(HTML.INPUT_ELEM);
+
+        AjaxContext ajaxContext = AjaxContext.getCurrentInstance(context);
+
+        if (ajaxContext.isAjaxRequest()) {
+            ajaxContext.addRenderedArea(id);
+        }
+
+    }
+}

Added: trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/box/Box.js
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/box/Box.js	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/box/Box.js	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,250 @@
+/**
+ * Box.js		Date created: 6.04.2007
+ * Copyright (c) 2007 Exadel Inc.
+ * @author Denis Morozov <dmorozov at exadel.com>
+ */
+ClientUILib.declarePackage("ClientUI.common.box.Box");
+
+
+/*
+ * Base class for all ui controls 
+ *
+ * TODO: description of control 
+ *
+ * TODO: usage description
+ * Usage: 
+ *  ClientUILib.declarePackage("ClientUI.common");
+ *  ClientUILib.requireClass("ClientUI.common.box.Box");
+ * 	var ClientUI.MyControl = Class.create({
+ * 		CLASSDEF : {
+ * 			name:  'ClientUI.MyControl',
+ * 			parent: ClientUI.common.box.Box
+ * 		}, 		
+ *		initialize:function() {
+ *			this.parentClass().constructor().call(this)
+ *			alert("A new " + this.getClass().className + " was created")
+ *		}				
+ *	})
+ */
+ClientUI.common.box.Box = Class.create({
+
+	initialize: function(element, parentElement, dontUpdateStyles) {
+		this.element = $(element);
+		if(!this.element) {
+			this.element = $(document.createElement("div"));
+			if($(parentElement)) {
+      			$(parentElement).appendChild(this.element);
+			}
+      		else {
+	      		document.body.appendChild(this.element);
+      		}
+      	}
+		//http://jira.jboss.com/jira/browse/RF-2068
+		//this.element.wrapper = this;
+		if(!this.element.parentNode && $(parentElement)) {
+			$(parentElement).appendChild(this.element);
+		}
+
+      	if(!this.element.id) {
+			this.element.id = "ClientUI_Box" + ClientUI_common_box_Box_idGenerator++;
+		}
+		if(!dontUpdateStyles) {
+	      	this.element.setStyle({overflow: 'hidden'});
+	      	this.element.setStyle({whiteSpace: 'nowrap'});
+		}
+	},
+	
+	setParent: function(newParent) {
+		if(this.element.parentNode) {
+			this.element.parentNode.removeChild(this.element);
+		}
+		if(newParent) {
+			if(newParent.getElement) {
+				newParent = newParent.getElement();
+			}
+			$(newParent).appendChild(this.element);			
+		}
+		return this;
+	},
+	getElement: function() {
+		return this.element;
+	},
+	getHeight: function() {
+		var el = this.getElement();
+		if(el.tagName.toLowerCase() != "body") {
+			var h = el.offsetHeight;
+			return h>0 ? h : (this.element.boxHeight ? parseInt(this.element.boxHeight) : 0);
+		}
+
+		if (self.innerHeight) { // all except Explorer
+			return self.innerHeight;
+		}
+		else if (document.documentElement && document.documentElement.clientHeight) {
+			// Explorer 6 Strict Mode
+			return document.documentElement.clientHeight;
+		}
+		else if (document.body) { // other Explorers
+			return document.body.clientHeight;
+		}
+	},
+	isModified: false,
+	setHeight: function(newHeight) {
+		this.element.boxHeight = newHeight;
+		if(Validators.IsNumber(newHeight)) {
+			if(newHeight<0) newHeight = 0;
+			newHeight += "px";	
+		}
+		this.element.setStyle({height: newHeight});
+		isModified = true;
+		return this;
+	},
+	getWidth: function() {
+		var el = this.getElement();
+		if(el.tagName.toLowerCase() != "body") {
+			var w = el.offsetWidth;
+			return w>0 ? w : (this.element.boxWidth ? parseInt(this.element.boxWidth) : 0);
+		}
+			
+		if (self.innerHeight) {// all except Explorer
+			return self.innerWidth;
+		}
+		else if (document.documentElement && document.documentElement.clientHeight) {
+			// Explorer 6 Strict Mode
+			return document.documentElement.clientWidth;
+		}
+		else if (document.body) { // other Explorers
+			return document.body.clientWidth;
+		}			
+	},
+	setWidth: function(newWidth) {
+		this.element.boxWidth = newWidth;
+		if(Validators.IsNumber(newWidth)) {
+			if(newWidth<0) newWidth = 0;
+			newWidth += "px";	
+		}
+		this.element.setStyle({width: newWidth});
+		isModified = true;
+		return this;
+	},
+	moveToX: function(x) {
+		if(Validators.IsNumber(x)) {x += "px";}
+		this.getElement().setStyle({left: x});
+		isModified = true;
+		return this;		
+	},
+	moveToY: function(y) {
+		if(Validators.IsNumber(y)) {y += "px";}
+		this.getElement().setStyle({top: y});
+		isModified = true;
+		return this;
+	},
+	moveTo: function(x, y) {
+		this.moveToX(x);
+		this.moveToY(y);
+		return this;
+	},
+	hide: function() {
+		Element.hide(this.element);
+		isModified = true;
+		return this;
+	},
+	show: function() {
+		Element.show(this.element);
+		isModified = true;
+		return this;
+	},
+	updateLayout: function() {
+		isModified = false;
+		return this;
+	},
+	getViewportWidth: function() {
+		if(this.getElement().tagName.toLowerCase() != "body") {
+			var width = 0;
+			if( this.getElement().clientWidth ) {
+			    width = this.getElement().clientWidth;
+			}
+			else if( this.getElement().innerWidth ) {
+			    width = this.getElement().innerWidth - getScrollerWidth();
+			}
+			  
+			if(ClientUILib.isGecko) {
+			  	width -= this.getPadding("lr");
+			}
+			return width;
+		}
+		
+		return this.getWidth();
+	},
+	getViewportHeight: function() {
+		if(this.getElement().tagName.toLowerCase() != "body") {
+			var height = 0;
+			if( this.getElement().clientHeight ) {
+			    height = this.getElement().clientHeight;
+			}
+			else if( this.getElement().innerHeight ) {
+			    height = this.getElement().innerHeight - getScrollerWidth();
+			}
+			  
+			if(ClientUILib.isGecko) {
+			  	height -= this.getPadding("tb");
+			}
+			return height;
+		}
+		return this.getHeight();
+	},	
+	/**
+     * Gets the width of the border(s) for the specified side(s)
+     * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example, 
+     * passing lr would get the border (l)eft width + the border (r)ight width.
+     * @return {Number} The width of the sides passed added together
+     */
+    getBorderWidth : function(side){
+        return this.getStyles(side, this.borders);
+    },
+    
+    /**
+     * Gets the width of the padding(s) for the specified side(s)
+     * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example, 
+     * passing lr would get the padding (l)eft + the padding (r)ight.
+     * @return {Number} The padding of the sides passed added together
+     */
+    getPadding : function(side){
+        return this.getStyles(side, this.paddings);
+    },	
+	getStyles : function(sides, styles){
+        var val = 0;
+        for(var i = 0, len = sides.length; i < len; i++){
+             var w = parseInt(this.getElement().getStyle(styles[sides.charAt(i)]), 10);
+             if(!isNaN(w)) val += w;
+        }
+        return val;
+    },
+	makeAbsolute: function(keepPos) {
+		if(keepPos) {
+			Position.absolutize(this.getElement());	
+		}
+		else {
+			this.getElement().setStyle({position: 'absolute'});
+		}
+		return this;
+	},
+	getX: function() {
+		return this.getElement().offsetLeft;
+	},
+	getY: function() {
+		return this.getElement().offsetTop;
+	},
+	setStyle: function(style) {
+		this.getElement().setStyle(style);
+		return this;
+	},
+	
+	borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
+	paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
+	margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'}
+	
+});
+
+if(!ClientUI_common_box_Box_idGenerator) {
+var ClientUI_common_box_Box_idGenerator = 0;
+};
\ No newline at end of file

Added: trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/utils/StringBuilder.js
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/utils/StringBuilder.js	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/utils/StringBuilder.js	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,63 @@
+/*
+ * TODO: Copyright (c) 2007 Denis Morozov <dmorozov at exadel.com>
+ *
+ * ...
+ */
+ClientUILib.declarePackage("ClientUI.common.utils.StringBuilder");
+
+/*
+/* sbuilder.js - Helper class to improve strings concatenation perfomance 
+ * by Denis Morozov <dmorozov at exadel.com> distributed under the BSD license. 
+ *
+ * Usage:
+ * var sb = new StringBuilder();
+ * sb.append("String 1").append("String 2");
+ * sb.append("String 3");
+ * var str = sb.toString();
+ */
+StringBuilder = Class.create({
+	
+	initialize: function(str) {
+		this._string = null;
+		this._current = 0;
+		this._parts = [];
+		this.length = 0;
+		
+		if(str != null)
+			this.append(str);
+	},
+	append: function (str) {
+		// append argument
+		//this.length += (this._parts[this._current++] = String(str)).length;
+		this._parts.push(String(str));
+		
+		// reset cache
+		this._string = null;
+		return this;
+	},
+	
+	toString: function () {
+		if (this._string != null)
+			return this._string;
+		
+		var s = this._parts.join("");
+		this._parts = [s];
+		this._current = 1;
+		this.length = s.length;
+		
+		return this._string = s;
+	},
+	
+	clean: function(str) {
+		this.initialize();
+	}
+});
+
+Object.extend(StringBuilder.prototype, {
+	length: 	0,
+	
+	// private
+	_current:	0,
+	_parts:		[],
+	_string: 	null	// used to cache the string
+});

Added: trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/utils/Utils.js
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/utils/Utils.js	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/utils/Utils.js	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,313 @@
+var Utils = {
+	
+	DOM: {
+		copyAttributes : function(target, source, opts) {
+
+			//LOG.debug("copyAttributes");
+			var attrs = source.attributes;
+			
+			var exclusions = (opts && opts.exclude) ? opts.exclude : [];
+			
+			for(var i = 0 ; i < attrs.length; i++) {
+			
+				var attributeNode = attrs[i];
+				var nodeName = attributeNode.nodeName;
+				var nodeValue = attributeNode.nodeValue;
+				
+				var shouldCheckAttribute = 
+					nodeValue && 
+					nodeValue.length > 0 && 
+					exclusions.indexOf(nodeName) < 0;
+				
+				if (ClientUILib.isIE) {
+					shouldCheckAttribute &= attributeNode.specified;
+				}
+				
+				if(shouldCheckAttribute) {
+					
+					//LOG.debug("Copying attribute " + attributeNode.nodeName + "=" + attributeNode.nodeValue);
+					
+					var newAttributeNode = 
+						document.createAttribute(nodeName);
+					
+					newAttributeNode.nodeValue = attributeNode.nodeValue;
+					target.setAttributeNode(newAttributeNode);
+				}
+				
+			}
+			//LOG.debug("/copyAttributes");
+		},
+		
+		replaceNode : function(id, request) {
+			var target = document.getElementById(id);
+			var src = request.getElementById(id);
+	
+			if(target && src) {
+				var cells = target.cells;
+				for(var i = 0; i < cells.length; i++) {
+					Utils.DOM.Event.removeListeners(cells[i]);
+				}
+				
+				if (ClientUILib.isIE) {
+					
+					var s = String();
+					var newOuterXml = "<table><tbody>" + src.xml + "</tbody></table>";
+					var newNode = document.createElement("DIV");
+
+					newNode.innerHTML = newOuterXml;
+
+					var imported = newNode.firstChild.firstChild.firstChild;
+					target.parentNode.replaceChild(imported, target);;
+					return imported;
+					
+//				} else if (ClientUILib.isGecko){
+//					//Mozill family
+//					var theDoc = document;
+//					
+//					Utils.DOM._clearAttributes(target);
+//					Utils.DOM.copyAttributes(target, src);
+//
+//					target.innerHTML = src.innerHTML;//nnerHTML.join("");
+//					return target;
+				} else {
+					//Fall back to DOM, and cross the fingers
+					src = document.importNode(src, true);
+					target.parentNode.replaceChild(src, target);
+					return src;
+				}
+			}
+			else {
+				if(!target)
+					ClientUILib.log(ClientUILogger.ERROR, "DOM Element with id " + id + " not found for update.");
+				if(!src) {
+					ClientUILib.log(ClientUILogger.ERROR, "RESPONSE Element with id " + id + " not found for update.");
+					
+					// cleanup destination
+					if(target) {
+						for(var i=0; i<target.cells.length; i++) {
+							target.cells[i].innerHTML = "";
+						}
+					}
+				}
+			}
+		},
+		
+		_clearAttributes : function(node) {
+
+			var attrs = node.attributes;
+			if (node.clearAttributes) {
+				node.clearAttributes();
+			} else {
+				while(node.attributes.length > 0) {
+					node.removeAttributeNode(node.attributes[0]);
+				}
+			}
+		},
+		
+		_formatNode : function(node) {
+			
+			var sb = new StringBuilder();
+			
+			sb.append("<").append(node.nodeName);
+			for (var i = 0; i < node.attributes.length; i++) {
+				var attr = node.attributes[i];
+				if (attr.specified) {
+					sb
+						.append(" ")
+						.append(attr.nodeName)
+						.append("=\"")
+						.append(attr.nodeValue)
+						.append("\" ");
+				}
+			}
+			
+			sb.append("/>");
+			
+			return sb.toString();
+			
+		},
+		
+		Event: {
+			
+			/**
+			 * cache listeners in element to kill them all on element ajax replace
+			 * @param {Object} element
+			 * @param {Object} event
+			 * @param {Object} handler
+			 * @param {Object} useCapture
+			 */
+			observe : function (element, event, handler, useCapture) {
+				if (true) {
+					if (!element._listeners) {
+						element._listeners = [];
+					}
+					
+					element._listeners[element._listeners.length] = 
+						{
+							event: event,
+							handler: handler,
+							useCapture: useCapture
+						};
+				}
+				Event.observe(element, event, handler, useCapture);
+			},
+			
+			stopObserving : function(element, event, handler, useCapture) {
+				
+				if(element._listeners) {
+					element._listeners = 
+						element._listeners.reject(
+							function(obj) {
+								return obj.event == event 
+									&& obj.handler == handler 
+									&& obj.useCapture == useCapture;
+							}
+						);
+				}
+				
+				Event.stopObserving(element, event, handler, useCapture);
+				
+			}, 
+			
+			removeListeners : function(element) {
+				if (element._listeners) {
+					var l = element._listeners.length;
+					for(var i = 0; i < l; i++) {
+						var listener = element._listeners[i];
+						Event.stopObserving(
+							element, 
+							listener.event, 
+							listener.handler, 
+							listener.useCapture);
+					}
+					
+					element._listeners = null;
+				}
+			}	
+		}
+	},
+	
+	AJAX : {
+		updateRows: function(options,request,grid,clientid, callbacks, callbacksPost){
+			var localOptions = options;
+			var rowCount = grid.getBody().templFrozen.getElement().rows.length;
+			var startRow = localOptions.startRow;
+			var count = localOptions.count;
+			var rowindex, i, el;
+			var dataModel = grid.dataModel;
+			var baseid = clientid;
+			
+			var countForUpdate = 0;
+			var rowsForUpdate = [];
+			
+			for(i=0; i<count; i++) {
+				rowindex = startRow + i;
+				if(rowindex >= rowCount){
+					 rowindex -= rowCount;
+				}
+				[":f:", ":n:"].unbreakableEach(
+					function(suffix) {
+						var id = [baseid,suffix,rowindex].join("");
+						var row = Utils.DOM.replaceNode(id, request);
+						
+						if (callbacks) {
+							// just suspend operation for future
+							if(!rowsForUpdate[i]) rowsForUpdate[i] = {};
+							rowsForUpdate[i][suffix] = {index : rowindex, row : row};
+							countForUpdate++;
+						}
+					} 
+				);
+			}
+			if (ClientUILib.isIE7) {
+				setTimeout(function()  {
+						var body = grid.getBody();
+						["fTable", "nTable"].unbreakableEach(
+							function(prop) {
+								body[prop].hide();
+								body[prop].show();
+							}
+						);
+					}
+					,50);
+			} 
+			
+			if (callbacks && countForUpdate>0) {
+				// process suspended processing
+				setTimeout(function(){
+					for(var i=0; i<count; i++) {
+						callbacks.unbreakableEach(
+							function(callback) {
+								if(rowsForUpdate[i]) {
+									//if(rowsForUpdate[i][":f:"].row) callback.call(grid, rowsForUpdate[i][":f:"]);
+									if(rowsForUpdate[i][":n:"].row) callback.call(grid, rowsForUpdate[i][":n:"]);
+								}
+							}
+						);
+					}
+					
+					if(callbacksPost) {
+						callbacksPost.unbreakableEach(
+							function(callback) {
+								callback.call(grid);
+							}
+						);
+					}
+				}, 100);
+			}
+	
+			grid.getBody()._onDataReady(localOptions);
+		}	
+	}
+};
+/*
+var _cAtt = Utils.DOM._clearAttributes;
+Utils.DOM._clearAttributes = function() {
+	return;
+	var d1 = new Date().getTime();
+	_cAtt.apply(Utils.DOM, arguments);
+	var d2 = new Date().getTime();
+	ClientUILib.log(ClientUILogger.INFO, "Utils.DOM._clearAttributes " + (d2 - d1) + "ms");
+};
+var cAtt = Utils.DOM._clearAttributes;
+Utils.DOM.copyAttributes = function() {
+	return;
+	var d1 = new Date().getTime();
+	cAtt.apply(Utils.DOM, arguments);
+	var d2 = new Date().getTime();
+	ClientUILib.log(ClientUILogger.INFO, "Utils.DOM.copyAttributes " + (d2 - d1) + "ms");
+};
+*/
+Utils.execOnLoad = function(func, condition, timeout) {
+	
+	if (condition()) {
+		func();		
+	} else {
+		window.setTimeout(
+			function() {
+				Utils.execOnLoad(func, condition, timeout);
+			},
+			timeout
+		);
+	}
+};
+Utils.Condition = {
+	ElementPresent : function(element) {
+		return function () {
+			var el = $(element);
+			return el && el.offsetHeight > 0;
+		};
+	}
+};
+
+Utils.trace = function(s) {
+	LOG.info(s + ": " + (new Date().getTime()- this._d) + "    ");
+	//window.status = s + ": " + (new Date().getTime()- this._d) + "    " + window.status;
+	this._d = new Date().getTime();
+};
+
+Array.prototype.unbreakableEach = function(f) {
+	for (var i = 0; i < this.length; i++) {
+		f(this[i], i);
+	}
+};
\ No newline at end of file

Added: trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/utils/Validators.js
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/utils/Validators.js	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/common/utils/Validators.js	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,122 @@
+/**
+ * Validators.js		Date created: 14.04.2007
+ * Copyright (c) 2007 Exadel Inc.
+ * @author Denis Morozov <dmorozov at exadel.com>
+ */
+ 
+var Validators = {
+	/**
+	 * Internet Explorer holds references to objects that are not actually javascript objects. So if we use the 
+	 * objects in javascript it will give error. But the typeof operator identifies them as javascript objects( problem!!!). 
+	 * Here we can use the isIEObject() function to identify those objects.
+	 */
+	isIEObject: function(a) {
+		return this.isObject(a) && typeof a.constructor != 'function';
+	},
+
+	/**
+	 * This function returns true if a is an array, meaning that it was produced by the Array constructor or by 
+	 * the [ ] array literal notation.
+	 */
+	isArray: function(a) {
+		return this.isObject(a) && a.constructor == Array;
+	},
+ 
+	/**
+	 * This function returns true if a is one of the Boolean values, true or false.
+	 */
+	isBoolean: function(a) {
+		return typeof a == 'boolean';
+	},
+	
+	getBoolean: function(val, defVal) {
+		if(this.isBoolean(val))
+			return val;
+		if(val == "true") return true;
+		else if(val == "false") return false;
+		
+		return defVal;
+	},
+
+	/**
+	 * This function returns true if a is an object or array or function containing no enumerable members.
+	 */
+	isEmpty: function(o) {
+		if (this.isObject(o)) {
+           for (var i in o) {
+                return false;
+           }
+		}
+		else if(this.isString(o) && o.length > 0) {
+			return false;
+		}
+		
+		return !this.IsNumber(o);
+	},
+
+	/**
+	 * This function returns true if a is a function. Beware that some native functions in IE were made to look 
+	 * like objects instead of functions. This function does not detect that.Netscape is better behaved in this regard.
+	 */
+	isFunction: function(a) {
+		return typeof a == 'function';
+	},
+
+	/**
+	 * This function returns true if a is the null value.
+	 */
+	isNull: function(a) {
+		return typeof a == 'object' && !a;
+	},
+
+	/**
+	 * This function returns true if <code>data</code> is a finite number. It returns false if <code>data</code> is NaN or Infinite.
+	 */
+	IsNumber: function(data) {
+		if(typeof data == 'number' && isFinite(data)) {
+			return true;
+		}
+		
+		// if it is a string that contains a number
+		var re = /(^-?[1-9](\d{1,2}(\,\d{3})*|\d*)|^0{1})$/;
+		if ( re.test(data) ) {
+			return true;
+		}
+		return false;
+	},
+	IsFormattedNumber: function(data) {
+		// Regular expression should match number with commas or not
+		//1. ^-? <-- '-' is optional at the beginning
+		//2. \d{1,3} <-- with or without comma, first 3 digits
+		//3. \d{1,3}(\,\d{3})* <-- with comma, at least one digit with max of three before repeating like ',ddd'
+		//4. \d+ <-- without comma, match any number of integer(shouldn't be though)
+		var re = /^-?(\d{1,3}|\d{1,3}(\,\d{3})*|\d*)$/g;
+		if ( ! re.test(data) ) {
+			return false;
+		}
+		return true;		
+	},
+
+	/**
+	 * This function returns true if a is an object, array, or function. It returns false if a is a string, 
+	 * number, Boolean, null, or undefined.
+	 */
+	isObject: function(a) {
+		return (typeof a == 'object' && !!a) || this.isFunction(a);
+	},
+
+	/**
+	 * This function returns true if a is a string.
+	 */
+	isString: function(a) {
+		return typeof a == 'string';
+	},
+
+	/**
+	 * This function returns true if a is the undefined value. You can get the undefined value from an uninitialized 
+	 * variable or from an object's missing member.
+	 */
+	isUndefined: function(a) {
+		return typeof a == 'undefined';
+	}
+};
\ No newline at end of file

Added: trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/controls/datatable/ExtendedDataTable.js
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/controls/datatable/ExtendedDataTable.js	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/controls/datatable/ExtendedDataTable.js	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,312 @@
+if (!window.ExtendedDataTable) window.ExtendedDataTable = {};
+
+ExtendedDataTable.DataTable = Class.create({
+	initialize : function(id, options) {
+		this.id = id;
+		this.groups = [];
+		
+		// register event handlers
+		this.options = options;
+		this.selectionManager = new ExtendedDataTable.SelectionManager(options, this);
+		
+		if (this.options.sortFunction) {
+			this.sortFct = this.options.sortFunction;
+			this.eventCellClicked = this.OnCellMouseClicked.bindAsEventListener(this);
+		}
+		this.onGroupToggleFct = this.options.onGroupToggleFunction;
+		if (this.options.onColumnResize != null){
+			this.onColumnResize = this.options.onColumnResize;
+			this.columnWidths = "";
+		}		
+		this.eventContainerResize = this.OnWindowResize.bindAsEventListener(this);
+		this.eventGroupRowClicked = this.OnGroupRowMouseClicked.bindAsEventListener(this);
+		ClientUILib.log(ClientUILogger.INFO, "[observe] resize");
+		Event.observe(window, "resize", this.eventContainerResize);
+		this.minColumnWidth = this.options.minColumnWidth;
+		
+		var grid = this;
+
+		Utils.execOnLoad(
+			function(){
+				grid.update(true);
+			},
+			Utils.Condition.ElementPresent(id+':header'), 5);
+	},
+	
+	_findParentElement: function(event, element) {
+		var el = null;
+		if(ClientUILib.isSafari) {
+			var targetCell = event.currentTarget;
+			if(targetCell && targetCell.tagName.toLowerCase() == element) {
+				el = targetCell;
+			} else {
+				var e = (event.target || event.srcElement);
+				while((e != null) && (e.tagName.toLowerCase() != element) && (e != document)){
+					e = e.parentNode;
+				}//while
+				if ((e) && (e.tagName.toLowerCase() == element)){
+					el = e;
+				}
+			}
+		} else {
+			el = Event.findElement(event, element);
+		}
+		return el;
+	},
+	
+	OnCellMouseClicked: function(event) {
+		ClientUILib.log(ClientUILogger.EVENT, "OnCellMouseClicked");
+		//this.showSplashScreen();
+		//get column id
+		var el = this._findParentElement(event, "th");
+		var columnId = (el) ? el.id : null;
+		
+		ClientUILib.log(ClientUILogger.INFO, "columnId: " + columnId);
+			
+		if (columnId && (columnId != "")){
+			this.showSplashScreen();
+			this.sortFct(event, columnId);		
+		}
+		Event.stop(event);	
+	},
+	
+	preSendAjaxRequest: function(){
+		ClientUILib.log(ClientUILogger.INFO, "preSendAjaxReqest");
+		//remove listeners
+		Event.stopObserving(window, 'resize', this.eventContainerResize);
+		Event.stopObserving(document, 'mousemove', this.header.eventSepMouseMove);
+		Event.stopObserving(document, 'mouseup', this.header.eventSepMouseUp);
+		//show splash screen
+		this.showSplashScreen();
+	},
+		
+	showSplashScreen: function(){
+		this.table.setStyle('visibility:hidden;');
+		this.mainDiv.getElement().removeClassName('extdt-splscr-hddn');
+		this.mainDiv.getElement().addClassName('extdt-splscr-vsbl');
+	},
+	
+	hideSplashScreen: function(){
+		this.table.setStyle('visibility:visible;');
+		this.mainDiv.getElement().removeClassName('extdt-splscr-vsbl');
+		this.mainDiv.getElement().addClassName('extdt-splscr-hddn');		
+	},	
+	
+	OnWindowResize: function(event) {
+		ClientUILib.log(ClientUILogger.EVENT, "OnWindowResize.");
+		this.updateLayout();
+	},
+	getColumnsNumber: function() {
+		return this.columnsNumber;
+	},
+	getColWidth: function(columnNumber) {
+		
+	},
+	getColumns: function() {
+		return this.cols;
+	},
+	OnGroupRowMouseClicked: function(event) {
+		
+		ClientUILib.log(ClientUILogger.EVENT, "OnGroupRowMouseClicked.");
+		var groupRow = this._findParentElement(event, "tr");
+		var bExpanded = !(groupRow.getAttribute('expanded') == 'true');
+		var sExpanded = bExpanded ? 'true' : 'false';
+		var groupIndex = parseInt(groupRow.getAttribute('groupindex'));
+		if (this.onGroupToggleFct){
+			this.onGroupToggleFct(event, groupIndex);
+		}
+		groupRow.setAttribute('expanded', sExpanded);
+		var imageDiv = groupRow.firstChild.firstChild.firstChild;
+		this.toggleImageSource(imageDiv);
+		this.setGroupExpanded(groupIndex, bExpanded);
+		Event.stop(event);
+	},
+	
+	toggleImageSource: function(imageDiv) {
+		var src = imageDiv.getAttribute('src');
+		var alternateSrc = imageDiv.getAttribute('alternatesrc');
+		imageDiv.setAttribute('src', alternateSrc);
+		imageDiv.setAttribute('alternatesrc', src);		
+	},
+	
+	setGroupExpanded: function(iGroupIndex, bValue) {
+		var group = this.groups[iGroupIndex];
+		
+		var sVisibility;
+		var sBorder;
+		var sEmptyCells;
+		
+		if (bValue) {
+			sVisibility = '';
+			sBorderColor = '';
+		}else{
+			sVisibility = 'none';
+			sBorderColor = 'transparent';
+		}
+		var size = group.size();
+		for (var i=0; i<size; i++) {
+			group[i].style.display = sVisibility;
+			if (ClientUILib.isIE7){
+				//prevent IE from showing borders of cells
+				//which parents have been hidden :|
+				var cells = group[i].childNodes;
+				for (var j=0; j<cells.length; j++) {
+					cells[j].style.borderColor = sBorderColor;
+				}
+			}
+		}
+	},
+	
+	createControls: function() {
+		var id = this.id;
+		this.table = new ClientUI.common.box.Box(this.id +":tu",null,true);
+		this.mainDiv = new ClientUI.common.box.Box(this.id,null,true);
+		this.outerDiv = new ClientUI.common.box.Box(this.id +":od",null,true);
+		this.tableB = new ClientUI.common.box.Box(this.id +":n",null,true);
+		this.fakeIeRow = $(this.id +":fakeIeRow");
+		this.fakeIeBodyRow = $(this.id +":body:fakeIeRow");
+		this.header = new ExtendedDataTable.DataTable.header(this.id +":header",this);
+		this.header.minColumnWidth = this.minColumnWidth;
+		this.cols = this.table.getElement().getElementsByTagName("col");
+		this.columnsNumber = this.cols.length/2;
+		this.scrollingDiv = new ClientUI.common.box.Box(this.id +":sd",null,true);
+		this.groupRows = [];
+		
+		if (ClientUILib.isOpera) {
+			//no overflow-x nor overflow-y in Opera
+			this.scrollingDiv.setStyle({overflow: 'scroll',
+				width: this.mainDiv.getWidth()
+			});
+			this.table.setStyle({width: this.mainDiv.getWidth()});
+		};
+		
+		var i = 0;
+		var groupRow = $(id+':group-row:'+i);
+		while (groupRow != null) {
+			this.groupRows[i] = groupRow;
+			Utils.DOM.Event.removeListeners(groupRow);
+			Utils.DOM.Event.observe(groupRow,'click',this.eventGroupRowClicked);
+			i++;
+			groupRow = $(id+':group-row:'+i);
+		}
+	},
+	getScrollbarWidth: function() {
+		var sd = this.scrollingDiv.getElement();
+		return sd.offsetWidth - sd.clientWidth;
+	},
+	validateColumnsWidth: function(columns,excessWidth) {
+		var i=1;
+		var endIndex = columns.length/2-1;
+		while ((i < endIndex) && (excessWidth > 0)) {
+			var col = columns[columns.length-i-1];
+			var spareWidth = col.offsetWidth - this.minColumnWidth;
+			if (spareWidth > excessWidth) {
+				col.width = col.offsetWidth - excessWidth;
+				columns[endIndex - i].width = col.width;
+				excessWidth = 0;
+			}else{
+				col.width = col.offsetWidth - spareWidth;
+				columns[endIndex - i].width = col.width;
+				excessWidth -= spareWidth;
+			}
+			i++;
+		}
+	},	
+	updateLayout: function() {
+		var table = this.table.getElement();
+		this.grid = table;
+		var outerDiv = this.outerDiv.getElement();
+		var cols = this.getColumns();
+		var header = this.header;
+		var scrollingDiv = this.scrollingDiv;
+		var mainDivHeight = this.mainDiv.getHeight();
+		var mainDivWidth = this.mainDiv.getWidth();
+		var headerChildren = header.getColumnCells();
+		
+		var footers = table.getElementsBySelector('tfoot');
+		var footerHeight = 0;
+		if (footers.size() > 0) {
+			footerHeight = footers[0].offsetHeight;
+		}
+		ClientUILib.log(ClientUILogger.INFO, 
+			"[updateLayout] footerHeight = "+
+			footerHeight+";"
+		);
+		ClientUILib.log(ClientUILogger.INFO, 
+			"[updateLayout] mainDivHeight = "+
+			mainDivHeight+";"
+		);
+		var columnsNumber = this.getColumnsNumber();		
+
+		var visibleHeaderWidth = this.header.getVisibleWidth();
+		var excessWidth = this.header.getVisibleWidth() - this.mainDiv.getWidth();
+		ClientUILib.log(ClientUILogger.INFO, 
+			"[updateLayout] getVisibleHeaderWidth() = "+
+			visibleHeaderWidth+";"
+		);
+		ClientUILib.log(ClientUILogger.INFO, 
+			"[updateLayout] mainDiv.getWidth() = "+
+			this.mainDiv.getWidth()+";"
+		);
+		ClientUILib.log(ClientUILogger.INFO, 
+			"[updateLayout] excessWidth1 = "+
+			excessWidth+";"
+		);
+		if (excessWidth > 0) {
+			var scrollbarExcess = scrollingDiv.getElement().offsetWidth - scrollingDiv.getElement().clientWidth;
+			excessWidth += scrollbarExcess;
+			this.validateColumnsWidth(cols,excessWidth);
+		}else{			
+			var excessWidth = visibleHeaderWidth - scrollingDiv.getElement().clientWidth;
+			ClientUILib.log(ClientUILogger.INFO, 
+				"[updateLayout] excessWidth2 = "+
+				excessWidth+";"
+			);			
+			if (excessWidth>0) {
+				this.validateColumnsWidth(cols,excessWidth);
+			}
+		}
+		ClientUILib.log(ClientUILogger.INFO, 
+			"scrollbarWidth = "+
+			this.getScrollbarWidth()+";"
+		);		
+		if (ClientUILib.isOpera) {
+			var newWidth = mainDivWidth - visibleHeaderWidth;
+			cols[columnsNumber-1].width = newWidth; 		
+			cols[cols.length-1].width = newWidth - this.getScrollbarWidth();
+		}else{
+			cols[columnsNumber-1].width = null;
+			cols[cols.length-1].width = null;
+		}
+		
+		scrollingDiv.setStyle('height:'+(mainDivHeight - header.getHeight() - footerHeight - 2) +'px;');
+
+		for (var i=0; i<headerChildren.length-1; i++) {
+			var headerChild = headerChildren[i];
+			var isSortable = headerChild.getAttribute('sortable');
+			if ((isSortable) && (isSortable.indexOf('true') == 0)) {
+				Utils.DOM.Event.removeListeners(headerChild);
+				Utils.DOM.Event.observe(headerChild, 'click',  this.eventCellClicked);
+			}
+		};
+		header.adjustSeparators();
+		this.hideSplashScreen();
+	},
+	update: function(refreshEvents) {
+		this.createControls();
+		if ( !ClientUILib.isIE7 ) {
+			if (this.fakeIeRow) {
+				this.table.getElement().deleteRow(this.fakeIeRow);	
+				this.fakeIeRow = null;
+			}
+			if (this.fakeIeBodyRow) {
+				this.tableB.getElement().deleteRow(this.fakeIeBodyRow);
+				this.fakeIeBodyRow = null;
+			}
+		}		
+		this.selectionManager.refreshEvents();
+		this.updateLayout();
+		this.selectionManager.restoreState();
+	}
+
+});
\ No newline at end of file

Added: trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/controls/datatable/ExtendedDataTableHeader.js
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/controls/datatable/ExtendedDataTableHeader.js	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/controls/datatable/ExtendedDataTableHeader.js	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,308 @@
+ExtendedDataTable.DataTable.header = Class.create(ClientUI.common.box.Box, {
+	// constructor
+	initialize: function($super, elementId, extDt) {
+		this.extDt = extDt;
+		this.extDtId = this.extDt.id;
+		$super(elementId,extDt,true);
+		
+		//register events
+		this.eventSepClick = this.OnSepClick.bindAsEventListener(this);
+		this.eventSepMouseDown = this.OnSepMouseDown.bindAsEventListener(this);
+		this.eventSepMouseMove = this.OnSepMouseMove.bindAsEventListener(this);
+		this.eventSepMouseUp = this.OnSepMouseUp.bindAsEventListener(this);		
+		
+		var showMenuFct = this.extDt.options.showMenuFunction;
+		if (showMenuFct) {
+			this.showMenuFct = showMenuFct;
+			this.menuImageMouseDown = this.OnMenuImageMouseDown.bindAsEventListener(this);
+		};
+		
+		this.createControl(elementId);
+	},
+	getVisibleWidth: function() {
+		var sum = 0;
+		var cols = this.getColumns();
+		for(var i=0; i<this.getColumnsNumber()-1; i++){
+			sum += parseInt(cols[i].width);
+		}
+		return sum;
+	},		
+	createControl: function(elementId) {
+		if(!elementId) {
+			errMsg = "Invalid id specified for ExtendedDataTableGridHeader.";
+			throw(errMsg);
+		}
+
+		if(!this.parseTemplate(elementId)) {
+			//TODO insert comment about header structure here
+			errMsg = "TODO insert commnet about header structure here";
+			throw(errMsg);
+		}
+		this.adjustSeparators();		
+	},
+	parseTemplate: function(template) {
+		if(!template) {
+			return false;
+		}
+		this.headerRow = new ClientUI.common.box.Box(this.extDtId +":headerRow",this.getElement(),true);
+		this.filterRow = new ClientUI.common.box.Box(this.extDtId +":filterRow",this.getElement(),true);
+		this.cols = this.extDt.table.getElement().getElementsByTagName("col");		
+		this.columnCells = this.headerRow.getElement().childElements();
+		this.columnsNumber = this.columnCells.length;
+		return true;
+	},
+	getColumns: function() {
+		return this.cols;
+	},
+	getColumnCells: function() {
+		return this.columnCells;
+	},
+	getColumnsNumber: function() {
+		return this.columnsNumber;
+	},
+	
+	getColumnWidth: function(columnNumber) {
+		if ((columnNumber < this.getColumnsNumber()) && (columnNumber >=0)) {
+			var col = this.columnCells[columnNumber];
+			if (col.offsetWidth) {
+				return col.offsetWidth;
+			}else{
+				//cols don't have offsetWidth in Opera
+				return parseInt(col.width);
+			}
+		}else{
+			return null;
+		}
+	},
+	getHeightWithoutFacets: function() {
+		return this.headerRow.getHeight() + this.filterRow.getHeight();
+	},
+	OnMenuImageMouseDown: function(event) {
+		ClientUILib.log(ClientUILogger.EVENT, "OnMenuImageMouseDown.");
+		
+		var el = this.extDt._findParentElement(event, "th");
+		var columnId = (el) ? el.id : null;
+		
+		if (columnId && (columnId != "")){
+			var menuId = "#" + columnId + "menu";
+			menuId = menuId.replace(/:/g,"\\:");
+			this.showMenuFct(event, columnId, menuId);
+		}
+		Event.stop(event);
+	},
+		
+	adjustSeparators: function() {
+		var columnCells = this.getColumnCells();
+		this._redrawTable(this.extDt.table.getElement());
+		this._redrawTable(this.extDt.tableB.getElement());
+		for (var i=0; i<columnCells.length-1; i++) {
+			var headerChild = columnCells[i];
+			var headerNextChild = columnCells[i+1];
+			var headerChildChildren = headerChild.childElements();
+			var sepSpan = headerChildChildren[2];
+			var headerRowHeight = this.headerRow.getHeight();
+			var headerRowY = this.headerRow.getY();
+			sepSpan.columnIndex = i;
+			var sd = sepSpan.getWidth()/2 + 1;
+			var dropSpanLeft = headerChildChildren[3];
+			var dropSpanRight = headerChildChildren[5];
+			var menuImage = headerChildChildren[7];
+			
+			//remove listeners
+			Utils.DOM.Event.removeListeners(menuImage);
+			Utils.DOM.Event.removeListeners(sepSpan);			
+			//add listeners
+			Utils.DOM.Event.observe(menuImage,'click',this.menuImageMouseDown);
+			Utils.DOM.Event.observe(sepSpan, 'click',  this.eventSepClick);
+			Utils.DOM.Event.observe(sepSpan, 'mousedown', this.eventSepMouseDown);
+			Utils.DOM.Event.observe(sepSpan, 'mousemove', this.eventSepMouseMove, true);
+			Utils.DOM.Event.observe(sepSpan, 'mouseup', this.eventSepMouseUp, true);
+
+			var spanLeft = headerNextChild.offsetLeft - sd;
+			sepSpan.setStyle('height:'+headerRowHeight+'px');
+			sepSpan.setStyle('top:'+headerRowY+'px');
+			sepSpan.setStyle('left:'+spanLeft+'px');			
+			menuImage.setStyle('top:' + headerRowY + 'px');
+			menuImage.setStyle('left:'+(spanLeft-menuImage.offsetWidth)+'px');
+			dropSpanLeft.setStyle('top:'+headerRowY+'px');
+			var w = headerChild.getWidth();
+			dropSpanLeft.setStyle('left:'+ (headerChild.offsetLeft) +'px');
+			dropSpanLeft.setStyle('height:'+headerRowHeight+'px');
+			dropSpanLeft.setStyle('width:'+(w/2)+'px');
+			dropSpanRight.setStyle('top:'+headerRowY+'px');
+			dropSpanRight.setStyle('left:'+ (headerChild.offsetLeft + w/2) +'px');
+			dropSpanRight.setStyle('height:'+headerRowHeight+'px');
+			dropSpanRight.setStyle('width:'+(w/2)+'px');
+		}
+		this.lastColWidth = this.getColumnWidth(this.getColumnsNumber()-1);
+		ClientUILib.log(ClientUILogger.INFO, 
+			"[adjustSeparators] lastColWidth = "+
+			this.lastColWidth+";"
+		);		
+		if (ClientUILib.isIE7){
+			this.lastColWidth -= 15;
+		}		
+	},
+	
+	OnSepClick: function(event) {
+		ClientUILib.log(ClientUILogger.EVENT, "OnSepClick.");
+		Event.stop(event);
+	},
+	
+	OnSepMouseDown: function(event) {
+		ClientUILib.log(ClientUILogger.EVENT, "OnSepMouseDown.");
+		Event.stop(event);
+		this.dragColumnInfo = {
+			srcElement: Event.element(event),
+			dragStarted: false,
+			mouseDown: true,
+			startX: Event.pointerX(event) - this.extDt.outerDiv.getX(),
+			originalX: 0
+		};
+		var srcElement = this.dragColumnInfo.srcElement;
+		this.maxDelta = this.getColumnWidth(this.getColumnsNumber()-1);
+		this.maxDelta -= this.extDt.getScrollbarWidth();
+		this.minDelta = this.minColumnWidth - this.getColumnWidth(srcElement.columnIndex);
+//		ClientUILib.log(ClientUILogger.INFO, 
+//			"[onSepMouseMove] minColumnWidth = "+this.minColumnWidth+";"
+//		);
+//		ClientUILib.log(ClientUILogger.INFO, 
+//			"[onSepMouseMove] minColumnWidth = "+this.minColumnWidth+";"
+//		);		
+//		ClientUILib.log(ClientUILogger.INFO, 
+//			"[onSepMouseMove] columnWidth = "+this.getColumnWidth(srcElement.columnIndex+1)+";"
+//		);		
+//		ClientUILib.log(ClientUILogger.INFO, 
+//			"[onSepMouseMove] mindelta = "+this.minDelta+";"
+//		);		
+		//this.dragColumnInfo.object = this.getColumns()[srcElement.columnIndex];
+		Event.observe(document, 'mousemove', this.eventSepMouseMove, true);
+		Event.observe(document, 'mouseup', this.eventSepMouseUp, true);
+	},
+	
+	_showSplitter: function(index) {
+		if(!this.columnSplitter) {
+			this._createSplitter();
+		}
+
+		var pos = this.dragColumnInfo.startX;
+		pos += 6; //6 stands for width of the separatorSpan
+		this.dragColumnInfo.originalX = pos;
+		this.columnSplitter.show();
+		this.columnSplitter.setHeight(this.extDt.scrollingDiv.getHeight()+
+			this.getHeightWithoutFacets()
+		);
+		this.columnSplitter.moveTo(pos, this.headerRow.getY());
+	},
+	_hideSplitter: function() {
+		if(this.columnSplitter) {
+			this.columnSplitter.hide();
+		}
+	},
+	_createSplitter: function() {
+		this.columnSplitter = new ClientUI.common.box.Box(this.extDtId +":cs", this.extDt.grid);
+		this.columnSplitter.makeAbsolute();
+		this.columnSplitter.setWidth(this.minColumnWidth);
+	},	
+	
+	OnSepMouseUp: function(event) {
+		var colsNumber = this.getColumnsNumber();
+		ClientUILib.log(ClientUILogger.EVENT, "OnSepMouseUp.");
+		Event.stop(event);
+		Event.stopObserving(document, 'mousemove', this.eventSepMouseMove);
+		Event.stopObserving(document, 'mouseup', this.eventSepMouseUp);
+		var cols = this.getColumns();
+		if(this.dragColumnInfo && this.dragColumnInfo.dragStarted) {
+
+			this.dragColumnInfo.dragStarted = false;
+			this.dragColumnInfo.mouseDown = false;
+
+			var delta = Event.pointerX(event) - 
+				this.dragColumnInfo.startX - 
+				this.extDt.outerDiv.getX();
+			if (delta < this.minDelta) {
+				delta = this.minDelta;
+			}
+			if (delta > this.maxDelta) {
+				delta = this.maxDelta;
+			}			
+			if (ClientUILib.isIE7) {
+				// sep span width
+				delta -= 6;
+			}
+			var columnIndex = this.dragColumnInfo.srcElement.columnIndex;
+			var newWidth = this.getColumnWidth(columnIndex) + delta;
+			
+			cols[columnIndex].width = newWidth;
+			cols[columnIndex+this.getColumnsNumber()].width = newWidth;
+
+			this.extDt.updateLayout();
+			if (this.extDt.onColumnResize){
+				//set properly value to this.columnWidths
+				this.extDt.columnWidths = "";
+				for (i=0; i<this.columnsNumber; i++){
+					this.extDt.columnWidths += "" + this.cols[i].width + ";";
+				}//for
+				this.extDt.onColumnResize(event, this.extDt.columnWidths);
+			}
+		}
+		this._hideSplitter();
+	},
+	
+	OnSepMouseMove: function(event) {
+		if(this.dragColumnInfo && this.dragColumnInfo.mouseDown) {
+			if(!this.dragColumnInfo.dragStarted) {
+				this.dragColumnInfo.dragStarted = true;
+				this._showSplitter(this.dragColumnInfo.srcElement.columnIndex);
+			}
+			var delta = Event.pointerX(event) - 
+				this.dragColumnInfo.startX - 
+				this.extDt.outerDiv.getX();
+			if (delta < this.minDelta) {
+				delta = this.minDelta;
+			}
+			if (delta > this.maxDelta) {
+				delta = this.maxDelta;
+			}
+			var x = this.dragColumnInfo.originalX + delta;
+			var finalX = x - this.minColumnWidth - 6;
+//			ClientUILib.log(ClientUILogger.INFO, 
+//				"[onSepMouseMove] maxDelta = "+this.maxDelta+";"
+//			);
+//			ClientUILib.log(ClientUILogger.INFO, 
+//				"[onSepMouseMove] originalX = "+this.dragColumnInfo.originalX+";"
+//			);			
+			ClientUILib.log(ClientUILogger.INFO, 
+				"[onSepMouseMove] delta = "+delta+";"
+			);
+			ClientUILib.log(ClientUILogger.INFO, 
+				"[onSepMouseMove] mindelta = "+this.minDelta+";"
+			);			
+//			ClientUILib.log(ClientUILogger.INFO, 
+//				"[onSepMouseMove] object.offsetWidth = "+
+//				parseInt(this.dragColumnInfo.object.width)+";"
+//			);
+//			ClientUILib.log(ClientUILogger.INFO, 
+//				"[onSepMouseMove] minColumnWidth = "+minColumnWidth+";"
+//			);			
+//			ClientUILib.log(ClientUILogger.INFO,
+//				"[onSepMouseMove] x = "+x+";"
+//			);
+//			ClientUILib.log(ClientUILogger.INFO,
+//				"[onSepMouseMove] finalX = "+finalX+";"
+//			);
+//			ClientUILib.log(ClientUILogger.INFO,
+//				"[onSepMouseMove] ----------------------------"
+//			);
+			this.columnSplitter.moveToX(finalX); 				
+			Event.stop(event);
+		}
+	},
+	_redrawTable: function(table) {
+		var tr = table.insertRow(0);
+		var td = tr.insertCell(0);
+		td.setAttribute("colspan", 5);
+		td.innerHTML = "FU SAFARI!!!";
+		table.deleteRow(tr);
+	}		
+});
\ No newline at end of file

Added: trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/controls/datatable/ExtendedDataTableSelection.js
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/controls/datatable/ExtendedDataTableSelection.js	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUI/controls/datatable/ExtendedDataTableSelection.js	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,594 @@
+/* Class taken from ScrollableDataTable - unmodified */
+ExtendedDataTable.Selection = Class.create({
+	initialize: function() {
+		this.ranges = [];
+	},
+
+	addId: function(id) {
+		id = parseInt(id);
+		if(this.isSelectedId(id))
+			return;
+		var i = 0;
+		while(i < this.ranges.length && id >= this.ranges[i++].indexes[1]);
+		i--;
+		if(this.ranges[i-1] && id==(this.ranges[i-1].indexes[1]+1) ) {
+			if(id==(this.ranges[i].indexes[0]-1)) {
+				this.ranges[i-1].indexes[1] = this.ranges[i].indexes[1];
+				this.removeRange(i);			
+			} else {
+				this.ranges[i-1].indexes[1]++;			
+			}
+		} else {
+			if(this.ranges[i]){
+				if(this.ranges[i] && id==(this.ranges[i].indexes[0]-1)) {
+					this.ranges[i].indexes[0]--;			
+				} else {
+					if(id==(this.ranges[i].indexes[1]+1)){
+						this.ranges[i].indexes[1]++;			
+					} else {
+						if(id<this.ranges[i].indexes[1]){
+							this.addRange(i, new ExtendedDataTable.Range(id, id));					
+						} else {
+							this.addRange(i + 1, new ExtendedDataTable.Range(id, id));					
+						}
+					}
+				}	
+			} else {
+				this.addRange(i, new ExtendedDataTable.Range(id, id));					
+			}	
+		} 			
+	},
+
+	addRange: function(index, range) {
+		var i = this.ranges.push(range) - 2;
+		if(index >= 0) {
+			while(i>=index)
+				this.ranges[i+1] = this.ranges[i--];
+			this.ranges[i+1] = range;
+		}
+	},
+
+	removeRange: function(index) {
+		var i = index + 1;
+		while(i!=this.ranges.length)
+			this.ranges[i-1] = this.ranges[i++];
+		this.ranges.pop();
+	},
+
+	isSelectedId: function(id) {
+		var i = 0;
+		while(i < this.ranges.length && id >= this.ranges[i].indexes[0]) {
+			if(id >= this.ranges[i].indexes[0] && id <= this.ranges[i].indexes[1]) {
+				return true;
+			} else {
+				i++;
+			}
+		}
+		return false;
+	},
+
+	getSelectedIdsQuantity: function() {
+		var number = 0;
+		for (var i = 0; i < this.ranges.length; i++) {
+			number+= this.ranges[i].size();
+		}
+		return number;
+	},
+	
+	size: function () {
+		return this.getSelectedIdsQuantity();
+	},
+	
+	removeId: function(id) {
+		id = parseInt(id);
+		if(!this.isSelectedId(id))
+			return;
+		var i = 0;
+		while(i < this.ranges.length && id > this.ranges[i++].indexes[1]);
+		i--;
+		if(this.ranges[i]) {
+			if(id==(this.ranges[i].indexes[1]) ) {
+				if(id==(this.ranges[i].indexes[0])){
+					this.removeRange(i);			
+				} else {
+					this.ranges[i].indexes[1]--;			
+				}
+			} else {
+				if(id==(this.ranges[i].indexes[0])){
+					this.ranges[i].indexes[0]++;			
+				} else {
+				this.addRange(i+1, new ExtendedDataTable.Range(id+1, this.ranges[i].indexes[1]));			
+				this.ranges[i].indexes[1] = id-1;
+				}
+			}
+		}		
+	},
+
+	getState: function() {
+		var s = this.clone();
+		return {
+			size: function() {
+					return s.size();
+			},
+			
+			each: function(iterator) {
+				s.each(iterator);
+  			},
+			
+			isSelected: function(id) {
+				return s.isSelectedId(id);
+  			}
+		};
+	},
+
+	clone: function() {
+		var ret =  Object.extend(new Object(),this);
+		ret.ranges = new Array(ret.ranges.length);
+		for (var i = 0; i < ret.ranges.length; i++) {
+			ret.ranges[i] = this.ranges[i].clone();
+		}		
+ 		return ret;
+ 	},
+
+	each: function(iterator) {
+		for (var i = 0; i < this.ranges.length; i++) {
+			this.ranges[i].each(iterator);					
+		}
+ 	},
+  	
+  	getRanges: function() {
+		return this.ranges;
+	},
+
+	setRanges: function(ranges) {
+		this.ranges = ranges;
+	},
+	
+	initRanges: function(rangeStrRArray) {
+		if(rangeStrRArray.length == 0) {
+			this.ranges = [];
+			return;
+		}
+		this.ranges = new Array(rangeStrRArray.length);
+		var indexStrRArray;
+		for(var i = 0; i < this.ranges.length; i++) {
+			indexStrRArray = rangeStrRArray[i].split(",");
+			this.ranges[i] = new ExtendedDataTable.Range(parseInt(indexStrRArray[0]), parseInt(indexStrRArray[1]));
+		}
+		
+	}, 
+
+	inspectRanges: function() {
+		var ranges = this.getRanges();
+		var ret = "";
+		ranges.each( function(r) { ret += r.inspect(); } );
+		return ret;
+	} 
+});
+
+
+/* Class taken from ScrollableDataTable - unmodified */
+ExtendedDataTable.Range = Class.create({
+	initialize: function(startIndex, endIndex) {
+		this.indexes = [startIndex, endIndex];
+	},
+
+	inspect: function() {
+		return this.indexes[0] + "," + this.indexes[1] + ";";
+	},
+	toString: function() {
+		return this.inspect();
+	},
+	
+	size: function() {
+		return this.indexes[1] - this.indexes[0] + 1;;
+	},
+	
+	each: function(iterator) {
+		var j = this.indexes[0];
+		while(j <= this.indexes[1]) {
+      		iterator(j++);					
+		}
+  	},
+	
+	clone: function() {
+		var ret = Object.extend(new Object(),this);
+		ret.indexes = this.indexes.clone();
+		return ret;
+  	}
+});
+
+/* Modified class from ScrollableDataTable */
+ExtendedDataTable.SelectionManager = Class.create({
+	initialize: function(options, owner) {
+		this.dataTable = owner;
+		this.options = options;
+		this.selectionFlag;
+		this.firstIndex;
+		this.activeRow = -1;
+		var element = options.gridId;
+		this.gridElement = document.getElementById(element + ":n");
+		
+		this.prefix = options.gridId;
+		this.selection = new ExtendedDataTable.Selection();
+
+		this.inputElement = options.selectionInput;
+		this.onselectionchange = options.onselectionchange;
+		this.selectedClass = options.selectedClass;
+		this.activeClass = options.activeClass;
+		
+
+	},
+	
+	refreshEvents: function() {
+		this.setListeners();
+		this.eventKeyPress = this.processKeyDown.bindAsEventListener(this);
+		Event.observe(document, "keydown", this.eventKeyPress);
+		A4J.AJAX.AddListener({
+			onafterajax: function(req, event, data) {
+				if(!$(this.prefix + ":n")) {
+					Event.stopObserving(document, "keydown", this.eventKeyPress);		
+				}
+			}.bind(this)
+		});
+		if (document.selection) {
+			Event.observe(this.gridElement, "click", this.resetSelection.bindAsEventListener(this));
+		}
+
+		this.eventLostFocus = this.processLostFocus.bindAsEventListener(this);
+		Event.observe(document, "click", this.eventLostFocus);
+
+		this.eventPreventLostFocus = this.processPreventLostFocus.bindAsEventListener(this);
+		Event.observe(this.gridElement, "click", this.eventPreventLostFocus);
+	},
+
+	restoreState: function() {
+		this.selectionFlag = null;
+		var selStrAr = $(this.inputElement).value.split(";");
+		var activeRow = NaN;
+		while (selStrAr.length != 0 && selStrAr[selStrAr.length - 1].indexOf(",") == -1 &&
+			isNaN(activeRow = Number(selStrAr.pop())));
+		if (!isNaN(activeRow)) {
+			this.setActiveRow(activeRow);
+		}
+		this.selection.initRanges(selStrAr);
+		var i = 0;
+		var j;
+		while(i < this.selection.ranges.length) {
+			j = this.selection.ranges[i].indexes[0];
+			while(j <= this.selection.ranges[i].indexes[1]) {
+				var nElement = $(this.prefix + ":n:" + j);
+				Element.addClassName(nElement, "extdt-row-selected");
+				Element.addClassName(nElement, "rich-sdt-row-selected");
+				Element.addClassName(nElement, this.selectedClass);
+				j++;
+			}
+			i++;
+		}
+		this.oldState = this.selection.getState();
+	},
+	
+	setListeners: function() {
+		//May need optimization by attaching listeners to whole rows instead of all cells
+		var nrows = $(this.prefix + ":n").rows;
+		this.rowCount = nrows.length;
+		var rowIndex;
+		var groupingExists = $(this.prefix + ":group-row:0") != null;
+		if(!groupingExists) { //simple listener binding
+			for(var i = 0; i < this.rowCount; i++) {
+				rowIndex = Number(nrows[i].id.split(this.prefix)[1].split(":")[2]);
+				this.addListener(nrows[i], rowIndex);
+			}
+		} else { //extended listener binding with grouping
+			var groupRow;
+			var lastGroupId = 0;
+			var bGroupExpanded;
+			if (!ClientUILib.isIE7) {
+				//make first fake row visible to ensure proper rendering
+				bGroupExpanded = true;
+			}else{
+				bGroupExpanded = false;
+			}
+			var groupId;
+			var groupItems = [];
+			var groupItem = 0;
+			var groups = [];
+			var groupRows = this.dataTable.groupRows;
+			for(var i = 0; i < this.rowCount; i++) {
+				tempo = nrows[i].id.split(this.prefix)[1];
+				var tempArr = tempo.split(":")
+				groupRow = tempArr[1] == "group-row";
+				groupId = Number(tempArr[2]);
+				if(groupRow) {
+					groups[lastGroupId] = groupItems;
+					bGroupExpanded = (groupRows[groupId].getAttribute('expanded') == 'true');
+					var textSpan = groupRows[lastGroupId].lastChild.lastChild;
+					var txtNode = document.createTextNode("(" + groupItems.size() + ")");
+					if (textSpan.lastChild) {
+						textSpan.replaceChild(txtNode, textSpan.lastChild);
+					}else{
+						textSpan.appendChild(txtNode);
+					}
+					groupItem = 0;
+					groupItems = [];
+					lastGroupId = groupId;
+				} else {
+					rowIndex = Number(nrows[i].id.split(this.prefix)[1].split(":")[2]);
+					this.addListener(nrows[i], rowIndex);
+					groupItems[groupItem++] = nrows[i];
+					if (!bGroupExpanded) {
+						nrows[i].style.display = 'none';
+						if (ClientUILib.isIE7){
+							//prevent IE from showing borders of cells
+							//which parents have been hidden :|
+							var cells = nrows[i].childNodes;
+							for (var j=0; j<cells.length; j++) {
+								cells[j].style.borderColor = 'transparent';
+							}
+						}						
+					}
+				}
+			}
+			groups[lastGroupId] = groupItems;
+			var textSpan = groupRows[lastGroupId].lastChild.lastChild;
+			var txtNode = document.createTextNode("(" + groupItems.size() + ")");
+			if (textSpan.lastChild) {
+				textSpan.replaceChild(txtNode, textSpan.lastChild);
+			}else{
+				textSpan.appendChild(txtNode);
+			}			
+			this.dataTable.groups = groups;
+		}
+	},
+	/*
+		Modification: instead of providing listener for each cell,
+		one is provided for a whole row
+	*/
+	addListener: function(tr, rowIndex) {
+		if (tr) {
+			var listener = this.processClick.bindAsEventListener(this, rowIndex);
+			Utils.DOM.Event.removeListeners(tr);
+			Utils.DOM.Event.observe(tr, "click", listener);	
+		}
+	},
+	
+/*	getGridSelection: function() {
+		return this.selection.getRanges();
+	},*/
+
+	processPreventLostFocus: function() {
+		this.inFocus = true;
+		this.preventLostFocus = true;
+	},
+
+	processLostFocus: function() {
+		if (!this.preventLostFocus) {
+			this.lostFocus();
+		} else {
+			this.preventLostFocus = false;
+		}
+	},
+
+	lostFocus: function() {
+		this.inFocus = false;
+	},
+
+	processKeyDown: function(event) {
+		if ($(this.prefix + ":n").rows.length > 0) {
+			if(!event.shiftKey) {
+				this.shiftRow = null;
+			}		
+			var range, rowIndex;
+			var activeRow = this.activeRow;
+			var noDefault = false;
+			this.firstIndex = Number($(this.prefix + ":n").rows[0].id.split(this.prefix)[1].split(":")[2]);;
+			switch (event.keyCode || event.charCode) {
+				case Event.KEY_UP:
+					if (this.inFocus && activeRow != null) {
+						if(this.firstIndex != activeRow) {
+							rowIndex = (this.rowCount + activeRow - 1) % this.rowCount;		
+							if (!event.ctrlKey && !event.shiftKey) {
+								this.selectionFlag = "x";
+								range = [rowIndex, rowIndex];
+								this.setSelection(range);		
+							} else if (!event.ctrlKey && event.shiftKey) {
+								if(!this.shiftRow) {
+									this.shiftRow = this.activeRow;
+								}
+								if(this.shiftRow >= this.activeRow) {
+									this.addRowToSelection(rowIndex);						
+								} else {
+									this.removeRowFromSelection(activeRow);						
+								}
+							}
+							noDefault = true;
+							this.setActiveRow(rowIndex);
+						} else {
+							this.grid.getBody().showRow("up");					
+						}
+					}
+					break;
+				case Event.KEY_DOWN:
+					if (this.inFocus && activeRow != null) {
+						rowIndex = (activeRow + 1) % this.rowCount;		
+						if(this.firstIndex != rowIndex) {
+							if (!event.ctrlKey && !event.shiftKey) {
+								this.selectionFlag = "x";
+								range = [rowIndex, rowIndex];
+								this.setSelection(range);		
+							} else if (!event.ctrlKey && event.shiftKey) {
+								if(!this.shiftRow) {
+									this.shiftRow = this.activeRow;
+								}
+								if(this.shiftRow <= this.activeRow) {
+									this.addRowToSelection(rowIndex);						
+								} else {
+									this.removeRowFromSelection(activeRow);						
+								}
+							}
+							noDefault = true;
+							this.setActiveRow(rowIndex);
+						} else {
+							this.grid.getBody().showRow("down");					
+						}
+					}
+					break;
+				case 65: case 97:								// Ctrl-A
+					if (this.inFocus && event.ctrlKey) {
+						this.selectionFlag = "a";
+						for (var i = 0; i <  this.rowCount; i++) {
+							this.addRowToSelection(i);
+						}
+						noDefault = true;
+					}
+					break;
+				case Event.KEY_TAB:
+					this.lostFocus();
+			}
+			if (noDefault) {
+				this.grid.getBody().showRow(this.activeRow);
+				this.selectionChanged(event);			
+				if (event.preventBubble) event.preventBubble();
+				Event.stop(event);
+			}
+		}
+	},
+	
+	/*
+		Modified method:
+		Component supports three selection modes:
+		none - no selection allowed
+		single - only one row can be selected at a time (no ctrl or shift)
+		multi - normal full-featured selection mode
+	*/
+	processClick: function(event, rowIndex) {
+		if (this.options.selectionMode == "none") {
+			return;
+		}
+
+		var bSingleSelection = this.options.selectionMode == "single";
+
+		if(!event.shiftKey) {
+			this.shiftRow = null;
+		}		
+		var range;	
+		
+		if ( event.shiftKey && !event.ctrlKey && !bSingleSelection && !event.altKey) {
+			this.firstIndex = Number($(this.prefix + ":n").rows[0].id.split(this.prefix)[1].split(":")[2]);;
+			this.selectionFlag = "x";
+			if(!this.shiftRow) {
+				this.shiftRow = this.activeRow;
+			}
+			this.startRow = this.shiftRow;
+			if (((this.startRow <= rowIndex) && (this.firstIndex <= this.startRow || rowIndex < this.firstIndex))
+				|| (this.startRow > rowIndex && this.firstIndex < this.startRow && rowIndex <= this.firstIndex)) {
+				this.endRow = rowIndex;
+			} else {
+				this.endRow = this.startRow;
+				this.startRow = rowIndex;
+			}
+			if(this.startRow > this.endRow) { //bugfix
+				//without this selection of first row with shift was broken
+				var t = this.startRow;
+				this.startRow = this.endRow;
+				this.endRow = t;
+			}
+			range = [this.startRow, this.endRow];
+			this.setSelection(range);		
+		} else if (!event.shiftKey &&  event.ctrlKey && !event.altKey) {
+			if (this.selection.isSelectedId(rowIndex)) {
+				this.removeRowFromSelection(rowIndex);
+			} else {
+				if (!bSingleSelection || this.selection.size() == 0) {
+					this.addRowToSelection(rowIndex);
+				}
+			}
+		} else  if (!event.shiftKey && !event.ctrlKey && !event.altKey) {
+			this.selectionFlag = "x";
+			range = [rowIndex, rowIndex];
+			this.setSelection(range);		
+		}
+		this.setActiveRow(rowIndex);
+		if (event.shiftKey) {
+			if (window.getSelection) {
+				window.getSelection().removeAllRanges();
+			} else if (document.selection) {
+				document.selection.empty();
+			}
+		}
+		this.selectionChanged(event);
+	},
+	
+	selectionChanged: function(event) {
+		$(this.inputElement).value = this.selection.inspectRanges() + this.activeRow + ";" + (this.selectionFlag ? this.selectionFlag : "") ;
+		var state = this.selection.getState();			
+		event.oldSelection = this.oldState;
+		event.newSelection = state;
+		if(this.onselectionchange) this.onselectionchange(event);
+		this.oldState = state;
+	},
+
+	setShiftRow: function(event) {
+		if(event.shiftKey) {
+			if(!this.shiftRow) {
+				this.shiftRow = this.activeRow;
+			}
+		} else {
+			this.shiftRow = null;		
+		}	
+	},
+	
+	setSelection: function(range) {
+		var i = range[0];
+		range[1] = (range[1] + 1) % this.rowCount;		
+		while (i != range[1]) {
+			this.addRowToSelection(i);
+			i = (i + 1) % this.rowCount;		
+		}
+		while (i != range[0]) {
+			this.removeRowFromSelection(i);
+			i = (i + 1) % this.rowCount;				
+		}
+	},
+	
+	resetSelection: function(e) {
+		if(e.shiftKey) {
+			document.selection.empty();
+		}
+	},
+	
+	/*
+		Row selection modified because we don't support frozen columns
+	*/
+
+	addRowToSelection: function(rowIndex) {
+		this.selection.addId(rowIndex);
+		var nElement = $(this.prefix + ":n:" + rowIndex);
+		Element.addClassName(nElement, "extdt-row-selected");
+		Element.addClassName(nElement, "rich-sdt-row-selected");
+		Element.addClassName(nElement, this.selectedClass);
+	},
+
+	removeRowFromSelection: function(rowIndex) {
+		this.selection.removeId(rowIndex);
+		var nElement = $(this.prefix + ":n:" + rowIndex);
+		Element.removeClassName(nElement, "extdt-row-selected");
+		Element.removeClassName(nElement, "rich-sdt-row-selected");
+		Element.removeClassName(nElement, this.selectedClass);
+	},
+
+	setActiveRow: function(rowIndex) {
+		var fElement, nElement;
+		if(this.activeRow != null) {
+			nElement = $(this.prefix + ":n:" + this.activeRow);
+			Element.removeClassName(nElement, "extdt-row-active");
+			Element.removeClassName(nElement, "rich-sdt-row-active");
+			Element.removeClassName(nElement, this.activeClass);
+		}
+		nElement = $(this.prefix + ":n:" + rowIndex);
+		Element.addClassName(nElement, "extdt-row-active");
+		Element.addClassName(nElement, "rich-sdt-row-active");
+		Element.addClassName(nElement, this.activeClass);
+		this.activeRow = rowIndex;
+	}
+});

Added: trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUILib.js
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUILib.js	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/javascript/ClientUILib.js	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,350 @@
+// ClientUILib base.js v1.0.0, Fri Jan 19 19:16:36 CET 2007
+
+// TODO: Copyright (c) 2007, Denis Morozov (dmorozov at exadel.com)
+// ...
+
+if(!ClientUILib) {
+
+var ClientUILib = {
+	Version: '1.0.0',
+	Name: 'ClientUILib',
+	LibraryPath: './',	
+	packages: [],
+	load: function(showLog) {
+	  // Check for Prototype JavaScript framework
+	  
+	  if((typeof Prototype=='undefined') || 
+	     (typeof Element == 'undefined') || 
+	     (typeof Element.Methods=='undefined') ||
+	     parseFloat(Prototype.Version.split(".")[0] + "." +
+	                Prototype.Version.split(".")[1]) < 1.5)
+	     throw("ClientUILib requires the Prototype JavaScript framework >= 1.5.0");
+	  	
+	  // Check for Extend JavaScript library
+//	  if((typeof Extend=='undefined') ||
+//	  	Extend.VERSION < 1.1)
+//	     throw("ClientUILib requires the Extend JavaScript library >= 1.1");
+
+	  $A(document.getElementsByTagName("script")).findAll( function(s) {
+	    return (s.src && s.src.match(/ClientUILib\.js(\?.*)?$/))
+	  }).each( function(s) {
+	    LibraryPath = s.src.replace(/ClientUILib\.js(\?.*)?$/,'');
+	  });
+	  
+	  if(showLog) {
+		  ClientUILogger.create("ClientUILogger");
+		  this.startTime = (new Date()).getTime();
+	  }
+	  
+	  this.initBrowser();
+	},
+ 	include: function(libraryPackageName) {
+		if(!this.packages)
+			this.packages=[];
+		if(!this.packages[libraryPackageName]) {
+			this.packages[libraryPackageName] = true;
+			var re = /\./g; // Replace all '.' in package name
+			var packagePath = LibraryPath + libraryPackageName.replace(re, "/");
+			document.write('<script type="text/javascript" src="' + packagePath + '.js"></script>');
+		}
+	},
+	include2: function(libraryPackageName) {
+		if(!this.packages)
+			this.packages=[];
+		if(!this.packages[libraryPackageName]) {
+			this.packages[libraryPackageName] = true;
+			var re = /\./g; // Replace all '.' in package name
+			var packagePath = LibraryPath + libraryPackageName.replace(re, "/");
+			var e = document.createElement("script");
+		   	e.src = packagePath+".js";
+		   	e.type="text/javascript";
+		   	document.getElementsByTagName("head")[0].appendChild(e);		
+		}
+	},
+	requireClass: function(libName) {
+		// required class not included before
+		if(!this.packages[libName]) {
+			//this.include2(libName);
+			ClientUILib.log(ClientUILogger.ERROR, "Library '" + libName + "' required!!!");
+			throw("Package '" + libName + "' is required!");
+		}
+	},
+	declarePackage: function(libName) {
+		var pckg = null;
+		var packages = $A(libName.split("."));
+		packages.each( function(s) {
+			if(pckg == null)
+				pckg = eval(s);
+			else {
+				if(!pckg[s]) pckg[s] = {};
+				pckg = pckg[s];
+			}
+	  	});
+	  	this.packages[libName] = true;
+	},
+	log: function(level, infoText) {
+		if(ClientUILogger.isCreated){
+			ClientUILogger.log(level, infoText);
+		} else {
+			switch(level) {
+				case ClientUILogger.INFO: LOG.info(infoText); break; 	
+				case ClientUILogger.ERROR: LOG.error(infoText); break; 	
+				case ClientUILogger.WARNING: LOG.warn(infoText); break; 	
+				default: LOG.a4jDebug(infoText);; 	
+			}
+		}
+	},
+	
+	initBrowser: function() {
+		var ua = navigator.userAgent.toLowerCase();
+		/** @type Boolean */
+		this.isOpera = (ua.indexOf('opera') > -1);
+	   	/** @type Boolean */
+		this.isSafari = (ua.indexOf('webkit') > -1);
+	   	/** @type Boolean */
+		this.isIE = (window.ActiveXObject);
+	   	/** @type Boolean */
+		this.isIE7 = (ua.indexOf('msie 7') > -1);
+	   	/** @type Boolean */
+		this.isGecko = !this.isSafari && (ua.indexOf('gecko') > -1);
+		
+		if(ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1){
+		    /** @type Boolean */
+		    this.isWindows = true;
+		}else if(ua.indexOf("macintosh") != -1){
+			/** @type Boolean */
+		    this.isMac = true;
+		}
+		if(this.isIE && !this.isIE7){
+	        try{
+	            document.execCommand("BackgroundImageCache", false, true);
+	        }catch(e){}
+	    }
+	}	
+};
+
+var ClientUILogger = {
+	// log level
+	INFO: 		1,
+	WARNING: 	2,
+	ERROR: 		3,
+	EVENT:		4, //KAW EVENT level added to trace events
+	ALERT:		5, //KAW ALERT level to stop executing script
+	hEnabledLevels: {
+		1: true,
+		2: true,
+		3: true,
+		4: true,
+		5: false
+	},	
+	// flag logger is initialized
+	isCreated: false,
+	width: 460,
+	height: 600,
+	top: 0,
+	left: 750,
+	bLoggingEnabled: true,
+	create: function() {
+		this.mainDiv = $(document.createElement("div"));
+		this.mainDiv.setStyle({border: '1px black solid',
+			position: 'absolute',padding: '1px'});
+		this.logElement = $(document.createElement("div"));
+		this.logElement.setStyle({overflow: 'auto', whiteSpace: 'nowrap'});
+		this.buttonsContainer = $(document.createElement("div"));
+		
+		var clearDiv = this.buttonClear = $(document.createElement('div'));
+		clearDiv.setStyle({width: 120 + 'px', height: 25 + 'px',
+			border: '1px black solid'});
+		clearDiv.innerHTML = 'Clear';
+		
+		var toggleLoggingDiv = this.buttonToggleLogging = $(document.createElement('div'));
+		toggleLoggingDiv.setStyle({width: 120 + 'px', height: 25 + 'px',
+			border: '1px black solid', position: 'relative',
+			top: '-27px', left: '122px'
+		});
+		toggleLoggingDiv.innerHTML = 'Logging '+this.isLoggingEnabled();
+		
+		var toggleAlertDiv = this.buttonToggleAlert = $(document.createElement('div'));
+		toggleAlertDiv.setStyle({width: 120 + 'px', height: 25 + 'px',
+			border: '1px black solid', position: 'relative',
+			top: '-54px', left: '244px'
+		});
+		toggleAlertDiv.innerHTML = 'Alert '+this.isLevelEnabled(ClientUILogger.ALERT);		
+		
+		this.buttonsContainer.appendChild(clearDiv);
+		this.buttonsContainer.appendChild(toggleLoggingDiv);
+		this.buttonsContainer.appendChild(toggleAlertDiv);
+		this.mainDiv.appendChild(this.logElement);
+		this.mainDiv.appendChild(this.buttonsContainer);		
+		
+		this.eventClearClicked = this.onClearClick.bindAsEventListener(this);
+		this.eventToggleLoggingClicked = this.onToggleLoggingClick.bindAsEventListener(this);
+		this.eventToggleAlertClicked = this.onToggleAlertClick.bindAsEventListener(this);
+		Event.observe(toggleLoggingDiv, 'click', ClientUILogger.eventToggleLoggingClicked);
+		Event.observe(toggleAlertDiv, 'click', ClientUILogger.eventToggleAlertClicked);
+		Event.observe(clearDiv, 'click', ClientUILogger.eventClearClicked);
+		Event.observe(window, 'load', ClientUILogger.init);
+		Event.observe(window, 'resize', ClientUILogger.resizeWindow);
+		
+		this.isCreated = true;
+	},
+	onToggleAlertClick: function() {
+		this.toggleLevel(ClientUILogger.ALERT);
+		this.buttonToggleAlert.innerHTML = 'Alert '+this.isLevelEnabled(ClientUILogger.ALERT);
+	},
+	onToggleLoggingClick: function(event) {
+		this.toggleLogging();
+		this.buttonToggleLogging.innerHTML = 'Logging '+this.isLoggingEnabled();
+	},
+	onClearClick: function(event) {
+		Event.stop(event);
+		this.logElement.innerHTML = '';
+	},
+	init: function() {
+		if(ClientUILogger.mainDiv)
+			document.body.appendChild(ClientUILogger.mainDiv);
+		ClientUILogger.show();
+	},
+	resizeWindow: function() {
+		ClientUILogger.show();
+	},
+	show: function() {
+		if(this.logElement) {
+			Element.show(this.mainDiv);
+			this.mainDiv.setStyle({width: this.width + 'px',
+				height: this.height + 'px', 
+				top: this.top + 'px',
+				left: this.left+ 'px',
+				zIndex: '1000'});
+			this.logElement.setStyle({width: '100%', height: '90%'});
+			this.buttonsContainer.setStyle({width: '100%', height: '10%'});			
+			//this.logElement.setStyle({top: (this.getWindowHeight() - this.height - 10) + 'px'});
+			//this.logElement.setStyle({top: 10 + 'px'});
+			//this.logElement.setStyle({left: (this.getWindowWidth() - this.width - 10) + 'px'});
+			//KAW changed logger display place
+		}
+	},
+	isLevelEnabled: function(level) {
+		return this.hEnabledLevels[level];
+	},
+	isLoggingEnabled: function() {
+		return this.bLoggingEnabled;
+	},
+	toggleLogging: function() {
+		this.bLoggingEnabled = !this.bLoggingEnabled;
+	},
+	toggleLevel: function(level) {
+		this.hEnabledLevels[level] = !this.hEnabledLevels[level];
+	},	
+	log: function(level, infoText) {
+		var bIgnoreLog = !this.isLoggingEnabled() || !this.isLevelEnabled(level);
+		if (bIgnoreLog) { 
+			//PREMATURE RETURN no logging required
+			return;
+		}
+		var msg = $(document.createElement("div"));
+		this.logElement.appendChild(msg);
+		msg.setStyle({width: '100%'});
+		
+		var font = "bold normal bold 10pt Arial";
+		var fontColor = "red";
+		
+		if (level == ClientUILogger.ALERT) {
+			alert(infoText);
+		}else{
+			switch(level) {
+				case ClientUILogger.INFO: 
+					fontColor = "black";
+					font = "normal normal normal 10pt Arial";
+					break;
+				case ClientUILogger.WARNING: 
+					fontColor = "blue";
+					font = "italic normal normal 10pt Arial";
+					break;
+				case ClientUILogger.ERROR: 
+					fontColor = "red";
+					font = "normal normal bold 10pt Arial";
+					break;
+				case ClientUILogger.EVENT: 
+					fontColor = "green";
+					font = "normal normal bold 10pt Arial";
+					break;
+				default:
+					infoText = "UNRESOLVED: level=" + level + ", msg=" + infoText;
+			}
+			msg.setStyle({font: font});
+			msg.setStyle({color: fontColor});
+			msg.appendChild(document.createTextNode("> " + infoText));
+			
+			this.logElement.scrollTop = this.logElement.scrollHeight;			
+		}
+	},
+	getWindowWidth: function(){
+	    var innerWidth;
+		  if (navigator.appVersion.indexOf('MSIE')>0) {
+			  innerWidth = document.body.clientWidth;
+	    } else {
+			  innerWidth = window.innerWidth;
+	    }
+	    return innerWidth;	
+	},
+	getWindowHeight: function(){
+	    var innerHeight;
+		  if (navigator.appVersion.indexOf('MSIE')>0) {
+			  innerHeight = document.body.clientHeight;
+	    } else {
+			  innerHeight = window.innerHeight;
+	    }
+	    return innerHeight;	
+	}	
+};
+
+ClientUILib.load(true); //KAW debugging ON
+
+// declare predefined packages
+var ClientUI = {
+	controls: {},
+	layouts: {}
+};
+
+// Some helper functions\
+if(!ClientUILib.isIE){
+	HTMLElement.prototype.click = function() {
+		var evt = this.ownerDocument.createEvent('MouseEvents');
+		evt.initMouseEvent('click', true, true, this.ownerDocument.defaultView, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
+		this.dispatchEvent(evt);
+	}
+};
+
+// Usage: Event.onReady(callbackFunction);
+Object.extend(Event, {
+	_domReady : function() {
+		if (arguments.callee.done) return;
+		arguments.callee.done = true;
+		 
+		if (Event._timer) clearInterval(Event._timer);
+		
+		Event._readyCallbacks.each(function(f) { f() });
+		Event._readyCallbacks = null;
+	},
+	onReady : function(f) {
+		if (!this._readyCallbacks) {
+			var domReady = this._domReady;
+		
+			if (domReady.done) return f();
+		
+			if (document.addEventListener)
+				document.addEventListener("DOMContentLoaded", domReady, false);
+			if (/WebKit/i.test(navigator.userAgent)) {
+				this._timer = setInterval(function() {
+					if (/loaded|complete/.test(document.readyState)) domReady();
+				}, 10);
+			}
+			Event.observe(window, 'load', domReady);
+			Event._readyCallbacks = [];
+		}
+		Event._readyCallbacks.push(f);
+	}
+});
+
+};

Added: trunk/sandbox/ui/extendedDataTable/src/main/javascript/common/prototype/ext/extend.js
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/javascript/common/prototype/ext/extend.js	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/javascript/common/prototype/ext/extend.js	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,189 @@
+// vim: tw=80 ts=4 sw=4 noet
+// ----------------------------------------------------------------------------
+// Project   : Extend - Prototype OOP extension
+// URL       : <http://www.ivy.fr/js/extend>
+// ----------------------------------------------------------------------------
+// Author    : Sebastien Pierre                              <sebastien at ivy.fr>
+// License   : Revised BSD License
+// ----------------------------------------------------------------------------
+// Creation  : 08-Sep-2006
+// Last mod  : 17-Nov-2006
+// ----------------------------------------------------------------------------
+
+// The Extend object holds all the information required to implement the
+// inheritance and other OO-goodness.
+if(!window.Extend) {
+
+Extend = {
+	VERSION:           1.1,
+	CLASSDEF:          "CLASSDEF",
+	DELETE:            "DELETE",
+	// These are a list of methods of class instances that are reserved by the
+	// OO layer (see the reparent method for more info)
+	INSTANCE_RESERVED: {
+		CLASSDEF:    true,
+		getClass:    true,
+		parentClass: true
+	},
+
+	// Sets up a class
+	setupClass: function( _class, declaration )
+	{
+		// We create an empty prototype if the user did not provide one
+		declaration        = declaration || {}
+		_class.prototype   = declaration
+		// We clone the given method definition, because they will be augmented
+		// with the ones defined in the parent class
+		_class.methods     = {} 
+		for ( var key in declaration ) { _class.methods[key] = declaration[key] }
+		_class.inherited   = {}
+		_class.parentClass = undefined
+		if ( declaration[Extend.CLASSDEF] )
+		{ _class.className = declaration[Extend.CLASSDEF].name }
+		else
+		{ _class.className = undefined }
+		_class.subclasses  = _class.subclasses || []
+		_class.constructor = Extend.Operations.constructor
+		_class.reparent    = Extend.Operations.reparent
+		_class.method      = Extend.Operations.method
+		_class.update      = Extend.Operations.update
+		if ( declaration[Extend.CLASSDEF] )
+		{ _class.reparent(declaration[Extend.CLASSDEF].parent) }
+		// We update the class methods with an `ofClass` method that returns the
+		// class, so that instances will have a proper
+		declaration.getClass    = function() {return _class}
+		declaration.parentClass = function() {return this.getClass().parentClass}
+		declaration.parentCall  = function() {
+			var new_args = []
+			for ( var i=1;i<arguments.length;i++ ) {new_args.push(arguments[i])}
+			return this.parentClass().method(arguments[0]).apply(this, new_args)
+		}
+		declaration.setClass    = function(newClass) {
+			return this.getClass().parentClass
+		}
+		// We reparent the subclasses if any
+		for ( var i=0 ; i<_class.subclasses ; i++ ) {
+			_class.subclasses[i].reparent(_class)
+		}
+		return _class
+	},
+	// These are operations that will be "mixed-in" with the new classes
+	Operations: {
+		constructor: function() {
+			return this.prototype.initialize || function() {}
+		},
+		// Reparents this class
+		reparent: function( newParentClass )
+		{
+			if ( this.parentClass )
+			{
+				var this_index = this.subclasses.indexOf(this)
+				this.parentClass.subclasses.splice(this_index, 1)
+				for ( var method_name in this.inherited ) {
+					this.method(method_name, null, this.parentClass)
+				}
+			}
+			this.parentClass   = newParentClass
+			if ( !newParentClass ) return
+			var parent_methods = newParentClass.prototype
+			// We iterate on all the parent methods
+			for ( parent_method_name in parent_methods ) {
+				// If the method is a reserved one, we skip it
+				if ( Extend.INSTANCE_RESERVED[parent_method_name] == true ) { continue }
+				// If the method is not directly defined in the current class, we add it
+				if ( this.methods[parent_method_name] == undefined )
+				{
+					this.method( parent_method_name,
+						parent_methods[parent_method_name],
+						newParentClass.inherited[parent_method_name] || newParentClass
+					)
+				}
+			}
+			newParentClass.subclasses.push(this)
+		},
+		update: function(newPrototype) {
+			Extend.setupClass(this, newPrototype||this.prototype)
+		},
+		// Returns, sets or deletes a method in this class
+		method: function( name, body, declaredIn ) {
+			if ( body == undefined )
+			{
+				var method = this.prototype[name]
+				if ( name == undefined ) throw new Error("Method not found: "+name)
+				return method
+			}
+			else
+			{
+				declaredIn = declaredIn || this
+				// If the method is declared in this class
+				if ( declaredIn == this )
+				{
+					if ( body == Extend.DELETE ) {
+						delete this.methods[name]
+						delete this.inherited[name]
+						delete this.prototype[name]
+						// If the method is defined in the parent we set it
+						if ( this.parentClass ) {
+							var parent_method = this.parentClass.method(name)
+							if ( parent_method ) {
+								this.method(name, parent_method, this.parentClass.inherited[name] || this.parentClass)
+							}
+						}
+					} else {
+						this.methods[name]   = body
+						this.prototype[name] = body
+						delete this.inherited[name]
+					}
+				}
+				// Or if its declared in another class
+				else
+				{
+					if ( body == Extend.DELETE ) {
+						delete this.inherited[name]
+						delete this.methods[name]
+						delete this.prototype[name]
+						// If the method is defined in the parent we set it
+						if ( this.parentClass ) {
+							var parent_method = this.parentClass.method(name)
+							if ( parent_method ) {
+								this.method(name, parent_method, this.parentClass.inherited[name] || this.parentClass)
+							}
+						}
+					}
+					else {
+						if ( this.methods[name] == undefined ) {
+							this.inherited[name] = declaredIn
+							this.prototype[name] = body
+						}
+					}
+				}
+				for ( var i=0 ; i<this.subclasses.length ; i++ )
+				{
+					this.subclasses[i].method(name,body,declaredIn)
+				}
+			}
+		}
+	}
+}
+
+// In case prototype is not available, we use this instead
+try {
+	Class = Class
+} catch ( Error ) {
+	Class = {create:function() {return function() {this.initialize.apply(this, arguments)}}}
+}
+Class._create = Class.create
+Class.create  = function( declaration ) {
+	var new_class = Extend.setupClass(Class._create(declaration), declaration)
+	 // The following only works on FireFox
+	/*
+	new_class.watch("prototype", function(id,oldval,newval) {
+		new_class.prototype = newval
+		Extend.setupClass(new_class, newval)
+		return newval
+	})*/
+	return new_class
+};
+
+};
+// EOF

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/css/extendedDataTable.xcss
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/css/extendedDataTable.xcss	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/css/extendedDataTable.xcss	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,632 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<f:template xmlns:f='http:/jsf.exadel.com/template'
+   xmlns:u='http:/jsf.exadel.com/template/util' 
+   xmlns="http://www.w3.org/1999/xhtml" >
+   
+   
+   <f:verbatim>
+   	
+   	ul,ol,li{
+   		margin: 0;
+   		padding: 0;
+   	}
+	ol,ul{
+		list-style: none;
+	}
+	
+		/**
+		 * Body cell declaration
+		 * dr-sdt-bc - reqired
+		 * dr-sdt-bcDef - default, used if no custom styles defined in body template
+		 */
+		.dt-sdt-bc {
+			box-sizing: border-box;
+			-moz-box-sizing: border-box;
+			-moz-outline: none;
+			-moz-user-focus: normal;
+			cursor: default;
+			border-right: 1px solid;
+			border-bottom: 1px solid;
+		}	
+		
+		.extdt-dr-table {
+			table-layout: fixed;
+		}
+		
+		.extdt-table-filterrow{
+		}
+		
+		.dt-sdt-rb {
+			font-weight: normal;
+			white-space: nowrap;
+			box-sizing: border-box;
+			-moz-box-sizing: border-box;
+		}		
+		
+		.extdt-subheadercell {
+			overflow: hidden;
+		}
+		
+		.dt-sdt-bcbody {
+			cursor: default;
+			font-weight: normal;
+			white-space: nowrap;	
+			overflow: hidden;
+			-o-text-overflow: ellipsis;
+			text-overflow: ellipsis;
+			-moz-box-sizing: border-box;
+			-moz-outline: none;	
+			width: 100%;
+			height: 20px;
+		}
+
+		.extdt-dr-menucell{
+		}
+		
+		.extdt-dr-menucell: hover{
+			background-position: center right;
+			background-repeat: no-repeat;
+		}
+
+	.extdt-maindiv {
+		background-repeat:no-repeat;
+		background-position:center;
+	}
+	
+	.extdt-menu-div {
+		height: 23px;
+		position: absolute;
+		width: 20px;
+	}
+
+	.extdt-group-row {
+		height: 15px;
+	}
+	
+	.extdt-group-text {
+		font-family: Arial,Verdana,sans-serif;
+		font-size: 11px;
+		margin-left: 10px;
+	}	
+	
+	.extdt-group-cell {
+		height: 15px;
+		background-color: #eeeeee;
+		border-bottom: 1px #c0c0c0 solid;
+		border-top: 1px #c0c0c0 solid;
+		padding: 0px;
+	}
+	
+	.extdt-group-image-expanded {
+		height: 10px;
+		width: 10px;
+		background-repeat:no-repeat;
+		background-position:center;
+	}
+	
+	.extdt-group-image-folded {
+		height: 10px;
+		width: 10px;
+		background-repeat:no-repeat;
+		background-position:center;
+	}
+
+	.dr-sdt-hsplit {
+		width:1px;
+		border-right: 1px dashed;
+		cursor: e-resize;
+		z-index: 100;
+	}
+
+	.dt-menu{
+		border: 1px solid #718bb7;
+		z-index: 15000;
+		zoom: 1;
+		/*background: #f0f0f0 url(menu.gif) repeat-y;*/
+		background-color: #f0f0f0;
+		background-repeat: repeat-y;
+		padding: 2px;
+		position: absolute;
+	}
+	.dt-menu a{
+		text-decoration:none!important;
+	}
+	.dt-menu-list{
+		background: transparent none repeat scroll 0% 50%;
+		border: 0 none;
+	}
+	.dt-menu-list-item{
+		font: normal 11px tahoma,arial,sans-serif;
+		white-space: nowrap;
+		-moz-user-select: none;
+		-khtml-user-select: none;
+		display: block; 
+		padding: 1px;
+	}
+	.dt-menu a.dt-menu-item{
+		display: block;
+		line-height: 16px;
+		padding: 3px 21px 3px 3px;
+		white-space: nowrap;
+		text-decoration: none;
+		color: #222;
+		-moz-outline: 0 none;
+		outline: 0 none;
+		cursor: pointer;
+	}
+	.dt-menu-item-icon{
+		border: 0 none;
+		height: 16px;
+		padding: 0;
+		vertical-align: top;
+		width: 16px; 
+		margin:0 8px 0 0;
+		background-position: center;
+	}
+	.dt-menu-check-item .dt-menu-item-icon{
+		/*background: transparent url(unchecked.gif) no-repeat center;*/
+		background-color: transparent;
+		background-repeat: no-repeat;
+		background-position: center;
+	}
+	.dt-menu-item-checked .dt-menu-item-icon{
+		/*background-image: url(checked.gif);*/
+	}
+
+	/**
+	 * Header cells drop target
+	 */
+	.dt-sdt-hdrop {
+		cursor: pointer;
+		font-size: 1px;
+		width: 10px;
+		height: 13px;
+		top: 0;
+		/*overflow: hidden;*/
+		position: absolute;
+		display: block;
+		white-space: nowrap;
+		z-index: 61;
+	}
+	
+	.dt-sdt-hdrop-top {
+		/*background-image: url(col-move-top.gif);*/ 
+		background-repeat: no-repeat;
+		/*background-position: top center;*/
+		display: block;
+		width:100%;
+		height:50%;
+		position: relative; 
+		top: -9px;
+	}
+	
+	.dt-sdt-hdrop-top-left{
+		background-position: top left;
+		left: -5px;
+	}
+	
+	.dt-sdt-hdrop-top-right{
+		background-position: top right;
+		right: -4px;
+	}
+	
+	.dt-sdt-hdrop-bottom {
+		/*background-image: url(col-move-top.gif);*/ 
+		background-repeat: no-repeat;
+		/*background-position: bottom center;*/
+		display: block;
+		width:100%;
+		height:50%;
+		position: relative; 
+		top: 9px;
+	}
+	
+	.dt-sdt-hdrop-bottom-left{
+		background-position: bottom left;
+		left: -5px;
+	}
+	
+	.dt-sdt-hdrop-bottom-right{
+		background-position: bottom right;
+		right: -4px;
+	}
+	
+	.dt-cell-div {
+		cursor: default;
+		font-weight: normal;
+		white-space: nowrap;	
+		overflow: hidden;
+		-o-text-overflow: ellipsis;
+		text-overflow: ellipsis;
+		-moz-box-sizing: border-box;
+		-moz-outline: none;	
+		width: 100%;
+	}	
+	
+	.extdt-innerdiv{
+		top: 0px;
+		left: 0px;
+		position: absolute;
+	}
+	
+	.extdt-outerdiv{
+		position:relative;
+		padding: 1px;
+		width: 100%;
+	}
+	
+	/**
+	 * Header cells separator
+	 */
+	.dt-sdt-hsep {
+	/*	background-image: url(grid-split.gif);*/
+		background-position: center;
+		background-repeat: repeat-y;
+		cursor: e-resize;	
+		font-size: 1px;
+		width: 6px;
+		height: 13px;
+	    overflow: hidden;
+	    top: 0;
+		position: absolute;
+		display: block;
+		white-space: nowrap;
+		z-index: 60;
+	}
+	
+	.extdt-content{
+		overflow-x:hidden;
+		overflow-y:auto;
+	}
+	
+	.extdt-splscr-hddn{
+	}
+	
+	.extdt-splscr-vsbl{
+	}	
+	
+	</f:verbatim>
+
+<u:selector name=".extdt-menu-div" >
+	<u:style name="background-image">
+		<f:resource f:key="/org/richfaces/renderkit/html/images/s.gif"/>
+	</u:style>	
+</u:selector>
+
+<u:selector name=".dr-sdt-hsplit" >
+	<u:style name="background-image">
+		<f:resource f:key="/org/richfaces/renderkit/html/images/s.gif"/>
+	</u:style>	
+</u:selector>	
+
+<u:selector name=".dt-sdt-hdrop" >
+	<u:style name="background-image">
+		<f:resource f:key="/org/richfaces/renderkit/html/images/s.gif"/>
+	</u:style>	
+</u:selector>
+
+<u:selector name=".extdt-splscr-vsbl" >
+		<u:style name="border" value="solid">  <!--tableBorderColor-->
+			<f:verbatim skin="tableBorderWidth" />
+			<f:verbatim skin="tableBorderColor" />
+		</u:style>	
+</u:selector>
+
+<u:selector name=".extdt-dr-table" >
+	<u:style name="background-color" skin="tableBackgroundColor" />  <!--tableBackgroundColor-->
+	<!-- <u:style name="border-collapse" value="inherit" /> fix RF-535 -->
+	<u:style name="empty-cells" value="show" />
+	<u:style name="border-collapse" value="collapse" />
+</u:selector>
+
+<u:selector name=".extdt-dr-menucell: hover">
+	<u:style name="background-image">
+		<f:resource f:key="/org/richfaces/renderkit/html/images/menuHover.gif"/>
+	</u:style>
+</u:selector>
+
+<u:selector name=".extdt-maindiv">
+		<u:style name="border" value="solid">  <!--tableBorderColor-->
+			<f:verbatim skin="tableBorderWidth" />
+			<f:verbatim skin="tableBorderColor" />
+		</u:style>
+	<u:style name="background-image">
+		<f:resource f:key="/org/richfaces/renderkit/html/images/loading.gif"/>
+	</u:style>
+</u:selector>
+
+<u:selector name=".extdt-group-image-expanded">
+	<u:style name="background-image">
+		<f:resource f:key="/org/richfaces/renderkit/html/images/minusIcon.gif"/>
+	</u:style>
+</u:selector>
+
+<u:selector name=".extdt-group-image-folded">
+	<u:style name="background-image">
+		<f:resource f:key="/org/richfaces/renderkit/html/images/plusIcon.gif"/>
+	</u:style>
+</u:selector>
+	
+<u:selector name=".dt-menu">
+	<u:style name="background-image">
+		<f:resource f:key="/org/richfaces/renderkit/html/images/menu.gif"/>
+	</u:style>
+</u:selector>
+
+<u:selector name=".dt-menu-check-item .dt-menu-item-icon">
+	<u:style name="background-image">
+		<f:resource f:key="/org/richfaces/renderkit/html/images/unchecked.gif"/>
+	</u:style>
+</u:selector>
+
+<u:selector name=".dt-menu-item-checked .dt-menu-item-icon">
+	<u:style name="background-image">
+		<f:resource f:key="/org/richfaces/renderkit/html/images/checked.gif"/>
+	</u:style>
+</u:selector>
+
+<u:selector name=".dt-sdt-hdrop-top">
+	<u:style name="background-image">
+		<f:resource f:key="/org/richfaces/renderkit/html/images/col-move-top.gif"/>
+	</u:style>
+</u:selector>
+
+<u:selector name=".dt-sdt-hdrop-bottom">
+	<u:style name="background-image">
+		<f:resource f:key="/org/richfaces/renderkit/html/images/col-move-bottom.gif"/>
+	</u:style>
+</u:selector>
+	
+<u:selector name=".dt-sdt-hsep">
+	<u:style name="background-image">
+		<f:resource f:key="/org/richfaces/renderkit/html/images/sep-span.gif"/>
+	</u:style>
+</u:selector>
+   
+<u:selector name=".dr-table" >
+	<u:style name="border-top" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="border-left" value="solid"> <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="border-right" value="solid"> <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>	
+	<u:style name="background-color" skin="tableBackgroundColor" />  <!--tableBackgroundColor-->
+	<!-- <u:style name="border-collapse" value="inherit" /> fix RF-535 -->
+	<u:style name="empty-cells" value="show" />
+	<u:style name="border-collapse" value="collapse" />
+</u:selector>
+
+<u:selector name=".dr-table-cell" >
+	<u:style name="border-right" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="border-bottom" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="padding" value="4px 4px 4px 4px"/>
+	<u:style name="font-size" skin="generalSizeFont"/>  <!--generalSizeFont-->
+	<u:style name="color" skin="generalTextColor"/> <!--generalTextColor-->
+	<u:style name="font-family" skin="generalFamilyFont"/> <!--generalFamilyFont-->
+</u:selector>
+
+<u:selector name=".dr-subtable-cell" >
+	<u:style name="border-right" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="border-bottom" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="padding" value="4px 4px 4px 4px"/>
+	<u:style name="font-size" skin="generalSizeFont"/>  <!--generalSizeFont-->
+	<u:style name="color" skin="generalTextColor"/> <!--generalTextColor-->
+	<u:style name="font-family" skin="generalFamilyFont"/> <!--generalFamilyFont-->
+</u:selector>
+
+<u:selector name=".dr-table-header" >
+	<u:style name="background-color" skin="headerBackgroundColor"/> <!--headerBackgroundColor-->
+	<u:style name="background-image" > <!--from headerGradientColor to headerBackgroundColor-->
+    	<f:resource f:key="org.richfaces.renderkit.html.GradientA"/>
+	</u:style>
+	<u:style name="background-position" value="top left"/>
+	<u:style name="background-repeat" value="repeat-x"/>
+</u:selector>
+
+<u:selector name=".dr-table-header-continue" >
+	<u:style name="background-color" skin="headerBackgroundColor"/> <!--headerBackgroundColor-->
+</u:selector>
+
+<u:selector name=".dr-table-headercell" >
+	<u:style name="border-right" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="border-bottom" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="padding" value="4px 4px 4px 4px"/>
+	<u:style name="color" skin="headerTextColor"/> <!--headerTextColor-->
+	<u:style name="text-align" value="center"/>
+	<u:style name="font-weight" skin="headerWeightFont"/>
+	<u:style name="font-size" skin="generalSizeFont"/>  <!--generalSizeFont-->
+	<u:style name="font-family" skin="generalFamilyFont"/> <!--generalFamilyFont-->
+</u:selector>
+
+<u:selector name=".dr-table-subheader" >
+	<u:style name="background-color" skin="additionalBackgroundColor"/> <!--additionalBackgroundColor-->
+</u:selector>
+
+<u:selector name=".extdt-table-filterrow" >
+	<u:style name="background-color" skin="additionalBackgroundColor"/> <!--additionalBackgroundColor-->
+	<u:style name="border-top" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>	
+</u:selector>
+
+<u:selector name=".dr-table-thead" >
+	<u:style name="border-bottom" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+</u:selector>
+
+<u:selector name=".extdt-subheadercell" >
+	<u:style name="border-right" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="border-bottom" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>	
+	<u:style name="padding" value="4px 4px 4px 4px"/>
+	<u:style name="text-align" value="center"/>
+	<u:style name="font-size" skin="generalSizeFont"/>  <!--generalSizeFont-->
+	<u:style name="color" skin="generalTextColor"/> <!--generalTextColor-->
+	<u:style name="font-family" skin="generalFamilyFont"/> <!--generalFamilyFont-->
+	<u:style name="white-space" value="nowrap"/>
+</u:selector>
+
+<f:verbatim>
+	.dr-table-sortable-header {
+		background-position: right center;
+		background-repeat: no-repeat;
+		white-space : nowrap;
+	}
+	
+	.dr-table-cursor-pointer {
+		cursor: pointer;
+	}
+	
+	.rich-inplace-edit, .rich-inplace-view {
+		cursor: default;
+	}
+	
+	.dr-table-header-sort-img {
+		vertical-align: middle;
+	}
+</f:verbatim>
+
+<u:selector name=".dr-table-footer" >
+	<u:style name="background-color" skin="tableFooterBackgroundColor"/> <!--tableFooterBackgroundColor-->
+</u:selector>
+
+
+<u:selector name=".dr-table-footer-continue" >
+	<u:style name="background-color" skin="tableFooterBackgroundColor"/> <!--tableFooterBackgroundColor-->
+</u:selector>
+
+<u:selector name=".dr-table-footercell" >
+	<u:style name="border-right" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="border-bottom" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="padding" value="4px 4px 4px 4px"/>
+	<u:style name="color" skin="generalTextColor"/> <!--headerTextColor-->
+	<u:style name="text-align" value="left"/>
+	<u:style name="font-weight" skin="headerWeightFont"/>
+	<u:style name="font-size" skin="generalSizeFont"/>  <!--generalSizeFont-->
+	<u:style name="font-family" skin="generalFamilyFont"/> <!--generalFamilyFont-->
+</u:selector>
+
+<u:selector name=".dr-table-subfooter" >
+	<u:style name="background-color" skin="tableSubfooterBackgroundColor"/> <!--tableSubfooterBackgroundColor-->
+</u:selector>
+
+<u:selector name=".dr-table-subfootercell" >
+	<u:style name="border-right" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="border-bottom" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="padding" value="4px 4px 4px 4px"/>
+	<u:style name="text-align" value="left"/>
+	<u:style name="font-size" skin="generalSizeFont"/>  <!--generalSizeFont-->
+	<u:style name="color" skin="generalTextColor"/> <!--generalTextColor-->
+	<u:style name="font-family" skin="generalFamilyFont"/> <!--generalFamilyFont-->
+</u:selector>
+
+<u:selector name=".dr-table-firstrow .dr-table-cell" >
+<!--	<u:style name="border-top" value="2px solid">
+	  <f:verbatim skin="tableBorderColor" />
+	</u:style> -->
+</u:selector>
+
+<u:selector name=".dr-subtable-firstrow .dr-subtable-cell" >
+	<!-- <u:style name="border" value="2px solid #c0c0c0"/>-->
+</u:selector>
+
+<u:selector name=".dr-subtable-header" >
+	<u:style name="background-color" skin="additionalBackgroundColor"/> <!--additionalBackgroundColor-->
+</u:selector>
+
+<u:selector name=".dr-subtable-headercell" >
+	<u:style name="border-right" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="border-bottom" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="padding" value="4px 4px 4px 4px"/>
+	<u:style name="text-align" value="center"/>
+	<u:style name="font-size" skin="generalSizeFont"/>  <!--generalSizeFont-->
+	<u:style name="color" skin="generalTextColor"/> <!--generalTextColor-->
+	<u:style name="font-family" skin="generalFamilyFont"/> <!--generalFamilyFont-->
+</u:selector>
+
+
+<u:selector name=".dr-subtable-footer" >
+	<u:style name="background-color" skin="tableSubfooterBackgroundColor"/> <!--tableSubfooterBackgroundColor-->
+</u:selector>
+
+<u:selector name=".dr-subtable-footercell" >
+	<u:style name="border-right" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="border-bottom" value="solid">  <!--tableBorderColor-->
+		<f:verbatim skin="tableBorderWidth" />
+		<f:verbatim skin="tableBorderColor" />
+	</u:style>
+	<u:style name="padding" value="4px 4px 4px 4px"/>
+	<u:style name="text-align" value="left"/>
+</u:selector>
+
+<u:selector name=".rich-sort-asc">
+	<u:style name="background-image">
+		<f:resource f:key="org.richfaces.renderkit.html.iconimages.DataTableIconSortAsc"/>
+	</u:style>
+</u:selector>
+<u:selector name=".rich-sort-desc">
+	<u:style name="background-image">
+		<f:resource f:key="org.richfaces.renderkit.html.iconimages.DataTableIconSortDesc"/>
+	</u:style>
+</u:selector>
+<u:selector name=".extdt-row-selected">
+    <u:style name="background-color" skin="additionalBackgroundColor" />
+</u:selector>
+<u:selector name=".dr-sdt-row-active">
+    <u:style name="color" skin="tabDisabledTextColor" />
+</u:selector>
+
+</f:template>

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/checked.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/checked.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/col-move-bottom.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/col-move-bottom.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/col-move-top.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/col-move-top.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/columns.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/columns.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/columns.png
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/columns.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/grid3-hd-btn.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/grid3-hd-btn.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/group-by.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/group-by.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/group-by.png
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/group-by.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/hmenu-asc.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/hmenu-asc.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/hmenu-desc.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/hmenu-desc.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/item-over.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/item-over.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/loading.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/loading.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/menu-sort-asc.png
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/menu-sort-asc.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/menu-sort-desc.png
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/menu-sort-desc.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/menu.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/menu.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/menuHover.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/menuHover.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/minusIcon.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/minusIcon.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/plusIcon.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/plusIcon.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/s.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/s.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/sep-span.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/sep-span.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/unchecked.gif
===================================================================
(Binary files differ)


Property changes on: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/images/unchecked.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/scripts/dt-drag-indicator.js
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/scripts/dt-drag-indicator.js	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/scripts/dt-drag-indicator.js	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,209 @@
+DragIndicator = {
+
+    init: function(event) {
+    	var ieVersion = RichFaces.getIEVersion();
+		DragIndicator.isIE6 = (ieVersion && ieVersion < 7);
+    },
+
+    setContent: function(name, single, params) {
+		Element.clearChildren(this);
+
+		var p = DnD.getDnDDefaultParams(this);
+		
+		if (!p) {
+			p = {};
+		}
+
+		if (params) {
+			Object.extend(p, params);
+		}
+		
+		if (!p['marker']) {
+			if (p[name]) {
+				p['marker'] = p[name];
+			} else {
+				p['marker'] = this.markers[name];
+			}
+		}
+
+		var parts;
+
+		if (single) {
+			parts = this.indicatorTemplates['single'];
+		} else {
+			parts = this.indicatorTemplates['multi'];
+		}
+		
+		new Insertion.Top(this, parts.invoke('getContent', p).join(''));
+		
+		if (DragIndicator.isIE6) {
+			this.initIFrame();
+		}
+	},
+
+	show: function() {
+		if (!this.floatedToBody) {
+			if (!this.realParent) {
+				this.realParent = this.parentNode;
+				this._nextSibling = this.nextSibling;
+			}
+			this.realParent.removeChild(this);
+			document.body.appendChild(this);
+			this.floatedToBody = true;
+		}
+		Element.show(this);
+		this.style.position = 'absolute';
+	},
+
+	hide: function() {
+		Element.hide(this);
+		this.style.position = '';
+		this.offsets = undefined;
+		this.leave();
+		if (this.floatedToBody && this.realParent) {
+			document.body.removeChild(this);
+			if (this._nextSibling) {
+				this.realParent.insertBefore(this, this._nextSibling);
+			} else {
+				this.realParent.appendChild(this);
+			}
+			this.floatedToBody = false;
+		}
+	},
+
+	position: function(x, y) {
+		if (!this.offsets) {
+			Element.show(this);
+			this.style.position = 'absolute';
+		}		
+		Element.setStyle(this, {"left": x + "px", "top": y + "px"});
+	},
+
+	accept: function() {
+		Element.removeClassName(this, 'drgind_default');
+		Element.removeClassName(this, 'drgind_reject');
+		Element.addClassName(this, 'drgind_accept');
+
+		var acceptClass = this.getAcceptClass();
+		if (acceptClass) {
+			Element.addClassName(this, acceptClass);
+		}
+	},
+
+	reject: function() {
+		Element.removeClassName(this, 'drgind_default');
+		Element.removeClassName(this, 'drgind_accept');
+		Element.addClassName(this, 'drgind_reject');
+
+		var rejectClass = this.getRejectClass();
+		if (rejectClass) {
+			Element.addClassName(this, rejectClass);
+		}
+	},
+
+	leave: function() {
+		Element.removeClassName(this, 'drgind_accept');
+		//Element.removeClassName(this, 'drgind_reject');
+		//Element.addClassName(this, 'drgind_default');
+		Element.removeClassName(this, 'drgind_default');
+		Element.addClassName(this, 'drgind_reject');
+
+		var acceptClass = this.getAcceptClass();
+		var rejectClass = this.getRejectClass();
+		if (acceptClass) {
+			Element.removeClassName(this, acceptClass);
+		}
+		if (rejectClass) {
+			Element.removeClassName(this, rejectClass);
+		}
+	},
+
+	getAcceptClass: function() {
+		return this.ils_acceptClass;
+	},
+
+	getRejectClass: function() {
+		return this.ils_rejectClass;
+	},
+	
+	initIFrame: function() {
+		var iframe = document.createElement("iframe");
+		Element.addClassName(iframe, 'rich-dragindicator-iframe');
+		this.insertBefore(iframe, this.firstChild);
+		var table = iframe.nextSibling;
+		iframe.style.width = table.offsetWidth + "px";
+		iframe.style.height = table.offsetHeight + "px";
+	}
+};
+
+function createDragIndicator(elt, acceptClass, rejectClass) {
+    Object.extend(elt, DragIndicator);
+    elt.init();
+	
+	elt.ils_acceptClass = acceptClass;
+	elt.ils_rejectClass = rejectClass;
+}
+
+//	<table border="0" cellpadding="3" cellspacing="0" class="drgind_internal" style="height : 100%">
+//		<tr>
+//			<td class="drgind_marker">
+//			{marker}
+//			</td>
+//			<td class="drgind_text">
+//				<div class="drgind_wordcut drgind_text_content">
+//				{label}
+//				</div>
+//			</td>
+//		</tr>
+//	</table>
+//	<div class="drgind_shadow" />
+DefaultDragIndicatorView = 
+[
+	new E('table', 
+			{
+				'style':'height : 100%; z-index: 2;', 
+				'className':'drgind_internal', 
+				'cellspacing':'0', 
+				'cellpadding':'3', 
+				'border':'0'
+			},
+			[
+				new E('tbody',{},
+					[
+						new E('tr',{},
+							[
+								new E('td',
+										{
+											'className':'drgind_marker'
+										},
+										[
+											new ET(function (context) { return Richfaces.eval("marker", context)})
+										]
+								),
+								new E('td',
+										{
+											'className':'drgind_text'
+										},
+										[
+											new E('div',
+													{
+														'className':'drgind_wordcut drgind_text_content'
+													},
+													[
+														new ET(function (context) { return Richfaces.eval("label", context)})
+													]
+											)
+										]
+								)
+							]
+						)
+					]
+				)
+			]
+	), 
+	new E('div',
+			{
+				'className':'drgind_shadow'
+			}
+	)
+];

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/scripts/simple-draggable.js
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/scripts/simple-draggable.js	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/scripts/simple-draggable.js	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,121 @@
+DnD.G3SimpleDraggable  = Class.create();
+
+Object.extend(DnD.G3SimpleDraggable.prototype, DnD.Draggable.prototype);
+Object.extend(DnD.G3SimpleDraggable.prototype, {
+	initialize: function(id, options) {
+		this.id = $(id);
+		
+		if (!this.id) {
+			alert("drag: Element with [" + id + "] ID was not found in the DOM tree. Probably element has no client ID or client ID hasn't been written. DnD's disabled. Check please!");
+			return ;
+		}
+		
+		this.options = options;
+
+		this.dragIndicatorId = this.options.dragIndicator;
+
+        this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+			
+		Event.observe(this.id, "mousedown", this.eventMouseDown);
+		
+		this.form = this.id;
+		while (this.form && !/^form$/i.test(this.form.tagName)) {
+			this.form = this.form.parentNode;
+		}
+		
+		this.enableDraggableCursors(this.options.grab, this.options.grabbing);
+	},
+
+	getDnDDragParams: function() {
+		if (this.options.dndParams) {
+			return this.options.dndParams.parseJSON(EventHandlersWalk);
+		}
+		
+		return null;
+	},
+
+    getIndicator: function() {    	
+    	var dragIndicator = $(this.dragIndicatorId);
+        if (!dragIndicator) {
+            dragIndicator = this.getOrCreateDefaultIndicator();
+        }
+
+        return dragIndicator;
+    },
+
+    ondragstart : function(event, drag) {
+    	this.showDropTargets();
+		drag.params = this.options.parameters;
+		drag.params[this.id] = this.id;
+
+		this.setIndicator(event);
+		
+		this.getIndicator().leave();
+
+		//this.dragEnter(event);
+		
+		if (this.form) {
+			drag.params[this.form.id] = this.form.id;
+		}
+	},
+	
+	ondragend: function (event, drag) {
+		this.hideDropTargets();
+	},
+
+	getContentType: function() {
+		return this.options.dragType;
+	},
+
+	getDraggableOptions: function() {
+		return this.options;
+	},
+
+	initDrag: function(event) {
+		if (Event.isLeftClick(event)) {
+		  var src = Event.element(event);
+		  if(src.tagName && /^INPUT|SELECT|OPTION|BUTTON|TEXTAREA$/i.test(src.tagName)) 
+				return;
+
+			Event.stop(event);
+
+			this.startDrag(event);
+			//Event.observe(document, "mousemove", this.listenDragBound);
+			//Event.observe(document, "mouseup", this.stopListenDragBound);
+		}
+	},
+	
+	showDropTargets: function(){
+		var idStr = this.id.id;
+		var ind = idStr.lastIndexOf('_');
+		var prefix = idStr.substring(0,ind).replace("hdrag", "hdrop");
+		var spans = document.getElementsByTagName("span");
+		for(i=0;i<spans.length;i++){
+			var s = spans[i];
+			if (s.id.indexOf(prefix) == 0){
+				//s.style.zIndex = '70';
+				s.style.visibility = "visible";
+				//change dropzone style
+				s.childNodes[0].style.visibility="hidden";//top element
+				s.childNodes[1].style.visibility="hidden";//bottom element
+			}
+		}//for
+	},
+	
+	hideDropTargets: function(){
+		var idStr = this.id.id;
+		var ind = idStr.lastIndexOf('_');
+		var prefix = idStr.substring(0,ind).replace("hdrag", "hdrop");
+		var spans = document.getElementsByTagName("span");
+		for (i=0;i<spans.length;i++){
+			var s = spans[i];
+			if (s.id.indexOf(prefix) == 0){
+				//s.style.zIndex = '59';
+				s.style.visibility = "hidden";
+				//change dropzone style
+				s.childNodes[0].style.visibility="hidden";//top element
+				s.childNodes[1].style.visibility="hidden";//bottom element
+			}
+		}//for
+	}
+});
\ No newline at end of file

Added: trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/scripts/simple-dropzone.js
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/scripts/simple-dropzone.js	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/resources/org/richfaces/renderkit/html/scripts/simple-dropzone.js	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,99 @@
+DnD.G3SimpleDropZone  = Class.create();
+Object.extend(DnD.G3SimpleDropZone.prototype, DnD.Dropzone.prototype);
+Object.extend(DnD.G3SimpleDropZone.prototype, {
+	initialize: function(id, options) {
+		this.id = id;
+		var element = $(id);
+		
+		if (!element) {
+			alert("drop: Element with [" + id + "] ID was not found in the DOM tree. Probably element has no client ID or client ID hasn't been written. DnD's disabled. Check please!");
+			return ;
+		}
+		
+		this.element = element;
+		if (options.acceptedTypes) {
+			this.acceptedTypes = options.acceptedTypes;
+		} else {
+			this.acceptedTypes = [];
+		}
+	
+		if (options.typeMapping) {
+			this.typeMapping = options.typeMapping;
+		} else {
+			this.typeMapping = {};
+		}
+		
+		if (options.cursorTypeMapping) {
+			this.cursorTypeMapping = options.cursorTypeMapping;
+		} else {
+			this.cursorTypeMapping = {};
+		}
+		
+		this.mouseoverBound = this.mouseover.bindAsEventListener(this);
+		this.mouseoutBound = this.mouseout.bindAsEventListener(this);
+		this.mouseupBound = this.mouseup.bindAsEventListener(this);
+		
+		Event.observe(element, "mouseover", this.mouseoverBound);
+		Event.observe(element, "mouseout", this.mouseoutBound);
+		Event.observe(element, "mouseup", this.mouseupBound);
+		
+		
+			
+		this.options = options || {};
+		this.enableDropzoneCursors(options.acceptCursor,options.rejectCursor);
+		
+	},
+
+	getDropzoneOptions: function() {
+		return this.options;
+	},
+
+	getDnDDropParams: function() {
+		if (this.options.dndParams) {
+			return this.options.dndParams.parseJSON(EventHandlersWalk);
+		}
+		
+		return null;
+	},
+
+	mouseover: function(event) {
+		if (window.drag){
+			this.dragEnter(event);
+			//change dropzone style
+			this.element.childNodes[0].style.visibility="visible";//top element
+			this.element.childNodes[1].style.visibility="visible";//bottom element
+		}
+	},
+
+	mouseup: function(event) {
+		this.dragUp(event);
+	},
+
+	mouseout: function(event) {
+		if (window.drag){
+			this.dragLeave(event);
+			//change dropzone style
+			this.element.childNodes[0].style.visibility="hidden";//top element
+			this.element.childNodes[1].style.visibility="hidden";//bottom element
+		}
+	},
+	getAcceptedTypes: function() {
+		return this.acceptedTypes;
+	},
+	getTypeMapping: function() {
+		return this.typeMapping;
+	},
+	
+	getCursorTypeMapping: function() {
+		return this.cursorTypeMapping;
+	},
+	
+	drop: function(event, drag){
+		alert('I drop');	
+	},
+	onafterdrag: function(drag) {
+		if (this.options.onafterdrag) {
+			this.options.onafterdrag.call(this, drag);
+		}
+	}
+});
\ No newline at end of file

Added: trunk/sandbox/ui/extendedDataTable/src/main/templates/README
===================================================================

Added: trunk/sandbox/ui/extendedDataTable/src/main/templates/org/richfaces/htmlExtendedDataTable.jspx
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/main/templates/org/richfaces/htmlExtendedDataTable.jspx	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/main/templates/org/richfaces/htmlExtendedDataTable.jspx	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<f:root 
+	xmlns:f="http://ajax4jsf.org/cdk/template" 
+	xmlns:c=" http://java.sun.com/jsf/core" 
+	xmlns:ui=" http://ajax4jsf.org/cdk/ui"
+	xmlns:u=" http://ajax4jsf.org/cdk/u"
+	xmlns:x=" http://ajax4jsf.org/cdk/x"
+    xmlns:h="http://jsf.exadel.com/header"
+	xmlns:vcp="http://ajax4jsf.org/cdk/vcp"	
+	
+	class="org.richfaces.renderkit.html.ExtendedDataTableRenderer"
+	baseclass="org.richfaces.renderkit.AbstractExtendedTableRenderer"
+	component="org.richfaces.component.UIExtendedDataTable" 
+	>
+	<h:styles>
+		css/extendedDataTable.xcss,
+		/org/richfaces/renderkit/html/css/dragIndicator.xcss,
+		/org/richfaces/renderkit/html/css/menucomponents.xcss,
+	</h:styles>
+	<h:scripts>
+		new org.ajax4jsf.javascript.AjaxScript(),
+		new org.ajax4jsf.javascript.PrototypeScript(),
+		new org.ajax4jsf.javascript.DnDScript(),
+		
+		/org/richfaces/renderkit/html/scripts/utils.js,
+		/org/richfaces/renderkit/html/scripts/json/json-dom.js,
+		/org/richfaces/renderkit/html/scripts/json/json-mini.js,
+		/org/richfaces/renderkit/html/scripts/dnd/dnd-common.js,
+		/org/richfaces/renderkit/html/scripts/dnd/dnd-draggable.js,
+		/org/richfaces/renderkit/html/scripts/dnd/dnd-dropzone.js,
+		/org/richfaces/renderkit/html/scripts/browser_info.js,
+		
+		/org/richfaces/renderkit/html/scripts/menu.js,
+		/org/richfaces/renderkit/html/scripts/context-menu.js,
+		/org/ajax4jsf/javascript/scripts/form.js,
+		/org/richfaces/renderkit/html/scripts/form.js,
+		
+		/org/richfaces/renderkit/html/scripts/jquery/jquery.js,
+		/org/richfaces/renderkit/html/script/controlUtils.js,
+		
+		/org/richfaces/renderkit/html/scripts/extended-data-table.js,
+		/org/richfaces/renderkit/html/scripts/dt-drag-indicator.js,
+		
+		/org/richfaces/renderkit/html/scripts/simple-draggable.js,
+		/org/richfaces/renderkit/html/scripts/simple-dropzone.js
+	</h:scripts>
+	<f:clientid var="clientId"/>
+	
+	<jsp:scriptlet>
+     	<![CDATA[
+			variables.setVariable("columnsCount", getColumnsCount(component)+1);
+		]]>
+    </jsp:scriptlet>
+	
+	<div id="#{clientId}:dataTable_indicator" class="drgind_fly drgind_default drag_indicator" style="display: none;" >
+		<f:call name="encodeNamespace" />
+		
+		<script type="text/javascript">
+			var elt = $("#{clientId}:dataTable_indicator");
+			elt.markers = {};
+			elt.indicatorTemplates = {};
+			<f:call name="encodeDragDropChildScripts" />
+			createDragIndicator(elt, '', '');
+		</script>
+	</div>
+	<span id="#{clientId}:dataTable_indicator_span" />
+
+	<div
+		id="#{clientId}"
+		class="extdt-maindiv extdt-splscr-vsbl"
+		style="width:#{component.attributes['width']}; height:#{component.attributes['height']}"
+		>
+
+	<div id="#{clientId}:od" class="extdt-outerdiv"
+		style="width:100%; height:100%;"
+		>
+		<div id="#{clientId}:innerd" class="extdt-innerdiv" style="height:100%;width:100%;">
+		<div id="#{clientId}:cs" class="dr-sdt-hsplit" style="display: none;"/>
+		<table id="#{clientId}:tu"
+	    		class="extdt-dr-table rich-table #{component.attributes['styleClass']}"
+	    		style="visibility:hidden;"
+	    		width="100%"
+  			>
+				<f:call name="encodeTableStructure"/>
+				<f:call name="encodeHeader"/>
+			<tbody>
+				<tr>
+					<td colspan="#{columnsCount}" style="padding: 0px;">
+						<div id="#{clientId}:sd" class="extdt-content" style="height:50px;">
+							<table id="#{clientId}:n" width="100%"
+				    				class="extdt-dr-table rich-table #{component.attributes['styleClass']}" style="#{component.attributes['style']}" 
+			  						>
+								<f:call name="utils.encodePassThruWithExclusions">
+									<f:parameter value="height,value,name,type,id,class,rows,style,width" />
+								</f:call>			  						
+								<f:call name="encodeTableStructure"/>
+								<tbody id="#{clientId}:tb">								
+									<vcp:body>
+										<f:call name="encodeRows"/>
+									</vcp:body>
+								</tbody>
+							</table>
+						</div>
+					</td>
+				</tr>
+			</tbody>
+			<f:call name="encodeFooter"/>
+		</table>
+		<f:call name="contributorsEncodeHere" />
+		</div>
+		<script type="text/javascript">
+		//<![CDATA[
+		#{this:getJavaScriptVarName(context, component)} = #{this:createClientDataTable(context, component)};
+		#{this:getScriptContributions(context, component)};
+		//]]>
+		</script>
+	</div>
+	<f:call name="encodeTableMenu"/>
+	</div>
+</f:root>
\ No newline at end of file

Added: trunk/sandbox/ui/extendedDataTable/src/test/java/org/richfaces/component/JSFComponentTest.java
===================================================================
--- trunk/sandbox/ui/extendedDataTable/src/test/java/org/richfaces/component/JSFComponentTest.java	                        (rev 0)
+++ trunk/sandbox/ui/extendedDataTable/src/test/java/org/richfaces/component/JSFComponentTest.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -0,0 +1,53 @@
+/**
+ * License Agreement.
+ *
+ * Rich Faces - Natural Ajax for Java Server Faces (JSF)
+ *
+ * Copyright (C) 2007 Exadel, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+package org.richfaces.component;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import javax.faces.component.UIComponent;
+
+/**
+ * Unit test for simple Component.
+ */
+public class JSFComponentTest 
+    extends TestCase
+{
+    /**
+     * Create the test case
+     *
+     * @param testName name of the test case
+     */
+    public JSFComponentTest( String testName )
+    {
+        super( testName );
+    }
+
+
+    /**
+     * Rigourous Test :-)
+     */
+    public void testComponent()
+    {
+        assertTrue( true );
+    }
+}

Modified: trunk/sandbox/ui/pom.xml
===================================================================
--- trunk/sandbox/ui/pom.xml	2008-06-19 17:13:04 UTC (rev 9120)
+++ trunk/sandbox/ui/pom.xml	2008-06-19 19:21:31 UTC (rev 9121)
@@ -20,5 +20,6 @@
     <!--module>rex-button</module-->
     <module>sortableHeader</module>
     <module>hotKey</module>
+    <module>extendedDataTable</module>
   </modules>
 </project>
\ No newline at end of file

Modified: trunk/ui/dataTable/src/main/config/component/columnAttributes.ent
===================================================================
--- trunk/ui/dataTable/src/main/config/component/columnAttributes.ent	2008-06-19 17:13:04 UTC (rev 9120)
+++ trunk/ui/dataTable/src/main/config/component/columnAttributes.ent	2008-06-19 19:21:31 UTC (rev 9121)
@@ -119,4 +119,16 @@
 	<name>sortIconDescending</name>
 	<classname>java.lang.String</classname>
 	<description>Defines sort icon in descending order</description>
+</property>
+<property>
+	<name>label</name>
+	<classname>java.lang.String</classname>
+	<description>Column label for drag indicator. Usable only for extendedDataTable component</description>
+	<defaultvalue>""</defaultvalue>
+</property>
+<property>
+	<name>visible</name>
+	<classname>boolean</classname>
+	<description>TODO: </description>
+	<defaultvalue>true</defaultvalue>
 </property>
\ No newline at end of file

Modified: trunk/ui/dataTable/src/main/java/org/richfaces/component/UIColumn.java
===================================================================
--- trunk/ui/dataTable/src/main/java/org/richfaces/component/UIColumn.java	2008-06-19 17:13:04 UTC (rev 9120)
+++ trunk/ui/dataTable/src/main/java/org/richfaces/component/UIColumn.java	2008-06-19 19:21:31 UTC (rev 9121)
@@ -27,6 +27,7 @@
 import javax.el.ValueExpression;
 import javax.faces.FacesException;
 import javax.faces.component.UIComponent;
+import javax.faces.component.UIOutput;
 import javax.faces.context.FacesContext;
 
 import org.ajax4jsf.component.AjaxSupport;
@@ -59,6 +60,46 @@
 	public abstract String getSortIconDescending();
 	public abstract void setSortIconDescending(String sortIcon);
 	
+	public abstract boolean isVisible();
+	public abstract void setVisible(boolean visible);
+
+	//This code block adds support for visible attribute of the column component
+	@Override
+	public boolean isRendered() {
+		return super.isRendered() && isVisible();
+	}
+	
+	@Override
+	public void setRendered(boolean rendered) {
+		super.setRendered(rendered);
+	}
+	//
+	
+	//Extended data table supports label attribute also
+	/**
+	 * Get label for column. If label attribute is set returns attribute value,
+	 * otherwise try to get label from column header content.  
+	 * @return label for column
+	 */
+	//TODO what about extra "label" facet?
+	public String getColumnLabel(){
+		String label = (String)getAttributes().get("label");
+		if ((label == null) || (label.length() == 0)){
+			//try get header name
+			UIComponent hFacet = getHeader();
+			if ((hFacet != null) && (hFacet.isRendered()) 
+					/* added check for rendered attribute */ && (hFacet instanceof UIOutput)) {
+				UIOutput output = (UIOutput)hFacet;
+				
+				//TODO add conversion support here
+				label = (String)output.getValue();
+			}
+		}
+		return label;
+	}//getColumnLabel
+	
+	//
+	
 	public FilterField getFilterField(){
 		FilterField filterField = null;
 		MethodExpression filterMethod = getFilterMethod();




More information about the richfaces-svn-commits mailing list