You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by sv...@apache.org on 2012/02/05 23:02:55 UTC

[8/9] WICKET-4240 moved old tree together into extensions

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/LinkIconPanel.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/LinkIconPanel.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/LinkIconPanel.java
deleted file mode 100644
index 9ed959f..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/LinkIconPanel.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.markup.html.tree;
-
-import org.apache.wicket.MarkupContainer;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.model.IModel;
-
-/**
- * Simple panel that contains a link with icon and a link with a label.
- * 
- * @author Matej Knopp
- */
-public class LinkIconPanel extends LabelIconPanel
-{
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * Constructs the panel.
-	 * 
-	 * @param id
-	 *            component id
-	 * @param model
-	 *            model that is used to access the TreeNode
-	 * @param tree
-	 */
-	public LinkIconPanel(String id, IModel<Object> model, BaseTree tree)
-	{
-		super(id, model, tree);
-	}
-
-	/**
-	 * @see org.apache.wicket.markup.html.tree.LabelIconPanel#addComponents(org.apache.wicket.model.IModel,
-	 *      org.apache.wicket.markup.html.tree.BaseTree)
-	 */
-	@Override
-	protected void addComponents(final IModel<Object> model, final BaseTree tree)
-	{
-		BaseTree.ILinkCallback callback = new BaseTree.ILinkCallback()
-		{
-			private static final long serialVersionUID = 1L;
-
-			@Override
-			public void onClick(AjaxRequestTarget target)
-			{
-				onNodeLinkClicked(model.getObject(), tree, target);
-			}
-		};
-
-		MarkupContainer link = tree.newLink("iconLink", callback);
-		add(link);
-		link.add(newImageComponent("icon", tree, model));
-
-		link = tree.newLink("contentLink", callback);
-		add(link);
-		link.add(newContentComponent("content", tree, model));
-	}
-
-	/**
-	 * Handler invoked when the link is clicked. By default makes the node selected
-	 * 
-	 * @param node
-	 * @param tree
-	 * @param target
-	 */
-	protected void onNodeLinkClicked(Object node, BaseTree tree, AjaxRequestTarget target)
-	{
-		tree.getTreeState().selectNode(node, !tree.getTreeState().isNodeSelected(node));
-
-		if (target != null)
-		{
-			tree.updateTree(target);
-		}
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/LinkTree.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/LinkTree.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/LinkTree.java
deleted file mode 100644
index f60cc17..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/LinkTree.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.markup.html.tree;
-
-import javax.swing.tree.TreeModel;
-
-import org.apache.wicket.Component;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.model.IModel;
-
-/**
- * Simple tree component that provides node panel with link allowing user to select individual
- * nodes.
- * 
- * @author Matej Knopp
- */
-public class LinkTree extends LabelTree
-{
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * Construct.
-	 * 
-	 * @param id
-	 */
-	public LinkTree(String id)
-	{
-		super(id);
-	}
-
-	/**
-	 * 
-	 * Construct.
-	 * 
-	 * @param id
-	 * @param model
-	 *            model that provides the {@link TreeModel}
-	 */
-	public LinkTree(String id, IModel<? extends TreeModel> model)
-	{
-		super(id, model);
-	}
-
-	/**
-	 * 
-	 * Construct.
-	 * 
-	 * @param id
-	 * @param model
-	 *            Tree model
-	 */
-	public LinkTree(String id, TreeModel model)
-	{
-		super(id, new WicketTreeModel());
-		setModelObject(model);
-	}
-
-	/**
-	 * @see org.apache.wicket.markup.html.tree.BaseTree#newNodeComponent(java.lang.String,
-	 *      org.apache.wicket.model.IModel)
-	 */
-	@Override
-	protected Component newNodeComponent(String id, IModel<Object> model)
-	{
-		return new LinkIconPanel(id, model, LinkTree.this)
-		{
-			private static final long serialVersionUID = 1L;
-
-			@Override
-			protected void onNodeLinkClicked(Object node, BaseTree tree, AjaxRequestTarget target)
-			{
-				super.onNodeLinkClicked(node, tree, target);
-				LinkTree.this.onNodeLinkClicked(node, tree, target);
-			}
-
-			@Override
-			protected Component newContentComponent(String componentId, BaseTree tree,
-				IModel<?> model)
-			{
-				return new Label(componentId, getNodeTextModel(model));
-			}
-		};
-	}
-
-	/**
-	 * Method invoked after the node has been selected / unselected.
-	 * 
-	 * @param node
-	 * @param tree
-	 * @param target
-	 */
-	protected void onNodeLinkClicked(Object node, BaseTree tree, AjaxRequestTarget target)
-	{
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/LinkType.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/LinkType.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/LinkType.java
deleted file mode 100644
index a18fc12..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/LinkType.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.markup.html.tree;
-
-import org.apache.wicket.util.lang.EnumeratedType;
-
-/**
- * The type of junction links and node selection links.
- * <dl>
- * <dt>Regular link</dt>
- * <dd>Non-ajax link, always refreshes the whole page. Works with javascript disabled.</dd>
- * <dt>Ajax link</dt>
- * <dd>Links that supports partial updates. Doesn't work with javascript disabled</dd>
- * <dt>Ajax fallback link</dt>
- * <dd>Link that supports partial updates. With javascript disabled acts like regular link. The
- * drawback is that generated url (thus the entire html) is larger then using the other two</dd>
- * </dl>
- */
-public final class LinkType extends EnumeratedType
-{
-
-	/** partial updates with no fallback. */
-	public static final LinkType AJAX = new LinkType("AJAX");
-
-	/**
-	 * partial updates that falls back to a regular link in case the client does not support
-	 * javascript.
-	 */
-	public static final LinkType AJAX_FALLBACK = new LinkType("AJAX_FALLBACK");
-
-	/**
-	 * non-ajax version that always re-renders the whole page.
-	 */
-	public static final LinkType REGULAR = new LinkType("REGULAR");
-
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * Construct.
-	 * 
-	 * @param name
-	 *            the name of the type of the link
-	 */
-	public LinkType(String name)
-	{
-		super(name);
-	}
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/WicketTreeModel.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/WicketTreeModel.java b/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/WicketTreeModel.java
deleted file mode 100644
index 1d58644..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/WicketTreeModel.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.markup.html.tree;
-
-import javax.swing.tree.TreeModel;
-
-import org.apache.wicket.model.util.GenericBaseModel;
-
-/**
- * @author Timo Rantalaiho
- */
-public class WicketTreeModel extends GenericBaseModel<TreeModel>
-{
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * Construct.
-	 */
-	public WicketTreeModel()
-	{
-	}
-
-	/**
-	 * Construct.
-	 * 
-	 * @param treeModel
-	 */
-	public WicketTreeModel(final TreeModel treeModel)
-	{
-		setObject(treeModel);
-	}
-
-	/**
-	 * @see org.apache.wicket.model.util.GenericBaseModel#createSerializableVersionOf(java.lang.Object)
-	 */
-	@Override
-	protected TreeModel createSerializableVersionOf(TreeModel object)
-	{
-		return object;
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/package.html
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/package.html b/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/package.html
deleted file mode 100644
index 75c7655..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/package.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-   Licensed to the Apache Software Foundation (ASF) under one or more
-   contributor license agreements.  See the NOTICE file distributed with
-   this work for additional information regarding copyright ownership.
-   The ASF licenses this file to You under the Apache License, Version 2.0
-   (the "License"); you may not use this file except in compliance with
-   the License.  You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
--->
-<!DOCTYPE HTML PUBLIC "-//W3C/DTD HTML 3.2 Final//NL">
-<html>
-<head>
-<title>wicket.extensions.markup.html.tree package</title>
-</head>
-<body>
-<p>
-Package for Tree components.
-</p>
-</body>
-</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/base-tree-images.png
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/base-tree-images.png b/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/base-tree-images.png
deleted file mode 100644
index 361644d..0000000
Binary files a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/base-tree-images.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/base-tree.css
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/base-tree.css b/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/base-tree.css
deleted file mode 100644
index a5c0b9a..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/base-tree.css
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-table.wicket-tree-content {
-	border-collapse: collapse;
-	empty-cells: show;
-}
-
-table.wicket-tree-content,
-table.wicket-tree-content td,
-table.wicket-tree-content tr,
-table.wicket-tree-content th {
-	padding: 0;
-	margin: 0;
-}
-
-table.wicket-tree-content td.spacer {
-	width: 18px;
-	height: 100%;
-}
-
-table.wicket-tree-content td.spacer span,
-table.wicket-tree-content td.line span {
-	display: block;
-	width: 18px;
-	height: 18px;
-}
-
-table.wicket-tree-content td.line {
-	width: 18px;
-	height: 100%;
-	background: url("base-tree-images.png") repeat-y -36px center;
-}
-
-table.wicket-tree-content td.half-line {
-	width: 18px;
-	height: 100%;
-	background: url("base-tree-images.png") no-repeat -72px center;
-}
-
-table.wicket-tree-content a.junction-open,
-table.wicket-tree-content a.junction-closed,
-table.wicket-tree-content span.junction-corner {
-	width: 18px;
-	height: 18px;
-	display: block;
-	background: url("base-tree-images.png") no-repeat;
-	margin: 0;
-	padding: 0;
-	border-width: 0;
-}
-
-table.wicket-tree-content a.junction-open {
-	background-position: -18px center;
-}
-
-table.wicket-tree-content a.junction-closed {
-	background-position: 0 center;
-}
-
-table.wicket-tree-content span.junction-corner {
-	background-position: -54px center;
-}
-
-table.wicket-tree-content table.icon-panel {
-	border-collapse: collapse;
-	empty-cells: show;
-}
-
-table.wicket-tree-content table.icon-panel, 
-table.wicket-tree-content table.icon-panel tr,
-table.wicket-tree-content table.icon-panel td {
-	margin: 0;
-	padding: 0;
-}
-
-table.wicket-tree-content table.icon-panel img {
-	padding: 0;
-	margin: -1px 0 0 2px;
-	display: block;
-	border-width: 0;
-}
-
-table.wicket-tree-content.selected {
-	background-color: #E0E8FF;
-	font-weight: bold;	
-}
-
-table.wicket-tree-content.selected .content {
-	font-weight: bold;	
-}
-
-table.wicket-tree-content table.icon-panel a {
-	text-decoration: none;
-	color: #3311aa;
-}
-
-table.wicket-tree-content table.icon-panel a:hover {
-	text-decoration: underline;
-	color: #3311aa;
-}
-
-table.wicket-tree-content table.icon-panel img.icon {
-	cursor: pointer;
-}
-
-table.icon-panel {
-	width: 100%;
-}
-
-table.icon-panel td.content {
-	width: 100%;
-}
-
-table.icon-panel td.content a {
-	padding-right: 0.4em;
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/folder-closed.gif
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/folder-closed.gif b/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/folder-closed.gif
deleted file mode 100644
index eb12976..0000000
Binary files a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/folder-closed.gif and /dev/null differ

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/folder-open.gif
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/folder-open.gif b/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/folder-open.gif
deleted file mode 100644
index c5c3110..0000000
Binary files a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/folder-open.gif and /dev/null differ

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/item.gif
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/item.gif b/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/item.gif
deleted file mode 100644
index 42d7318..0000000
Binary files a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/item.gif and /dev/null differ

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/tree.js
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/tree.js b/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/tree.js
deleted file mode 100644
index 0273dfe..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/tree/res/tree.js
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-if (typeof(Wicket) == "undefined")
-	Wicket = { };
-
-Wicket.Tree = { };
-
-Wicket.Tree.askForReload = function()
-{
-   if (confirm("There was a problem updating the tree. It might be caused be the old page being cached by the browser. \n" +
-   "It is recommended to reload the page. Do you want to reload it?"))
-   {
-      window.location.reload();
-   }
-};
-
-Wicket.Tree.removeNodes = function(prefix, nodeList)
-{
-   var problem = false;
-   for (var i = 0; i < nodeList.length; i++)
-   {
-      var e = document.getElementById(prefix + nodeList[i]);
-      if (e != null)
-      {
-         e.parentNode.removeChild(e);
-      }
-      else
-      {
-         // while developing alert a warning
-         problem = true;
-         Wicket.Log.error("Can't find node with id " + prefix + nodeList[i] +
-         ". This shouldn't happen - possible bug in tree?");
-      }
-   }
-   if (problem == true)
-   {
-      Wicket.Tree.askForReload();
-   }
-};
-
-Wicket.Tree.createElement = function(elementId, afterId)
-{
-   var existing = Wicket.$(elementId);
-   if (typeof(existing) != "undefined" && existing != null)
-   {
-      Wicket.Tree.askForReload();
-   }
-
-   var after = document.getElementById(afterId);
-   var newNode = document.createElement(after.tagName);
-   newNode.setAttribute("id", elementId);
-
-   var p = after.parentNode;
-
-   for (var i = 0; i < p.childNodes.length; ++i)
-   {
-      if (after == p.childNodes[i])
-         break;
-   }
-   if (i == p.childNodes.length - 1)
-   {
-      p.appendChild(newNode);
-   }
-   else
-   {
-      p.insertBefore(newNode, p.childNodes[i + 1]);
-   }
-};
-
-Wicket.TreeTable = { };
-
-/* Javascript that resizes the tree table header so that it matches size of the content.
-   This is needed when the scrollbar next to content is show, so that the columns are 
-   properly aligned */
-Wicket.TreeTable.update = function(elementId)
-{
-
-   var element = document.getElementById(elementId);
-
-   if (element != null && typeof(element) != "undefined")
-   {
-
-      try
-      {
-
-         /// find the div containing the inner header div
-         var headerParent = element.getElementsByTagName("div")[1];
-
-         // find the inner header div
-         var header = headerParent.getElementsByTagName("div")[0];
-
-         // body div should be next div after header parent
-         var body = headerParent.nextSibling;
-
-         // interate until div is found
-         while (body.tagName != "DIV")
-         {
-            body = body.nextSibling;
-         }
-
-         // last check to find out if we are updating the right component
-         if (body.className == "wicket-tree-table-body")
-         {
-
-            // get the right padding from header - we need to substract it from new width
-            var padding;
-            if (document.defaultView && document.defaultView.getComputedStyle)
-            {
-               padding = document.defaultView.getComputedStyle(headerParent, '').getPropertyValue("padding-right");
-            } else if (headerParent.currentStyle)
-            {
-               padding = headerParent.currentStyle.paddingRight;
-            }
-            else
-            {
-               padding = 6;
-            }
-
-            padding = parseInt(padding, 10);
-
-            // set the new width
-            var w = (body.getElementsByTagName("div")[0].clientWidth - padding) + "px";
-
-            if (w == (-padding) + "px")
-            { // this can happen if the first row is hidden (e.g. rootless mode)
-               // try to get the width from second row
-               w = (body.getElementsByTagName("div")[1].clientWidth - padding) + "px";
-
-            }
-
-            if (w != "0px")
-            {
-               header.style.width = w;
-            }
-
-         }
-      }
-      catch (ignore)
-      {
-      }
-   }
-};
-
-Wicket.TreeTable.attached = new Object();
-
-Wicket.TreeTable.attachUpdate = function(treeTableId)
-{
-   // get the object that contains ids of elements on which the update method was already attached
-   var attached = Wicket.TreeTable.attached;
-
-   // force updating the element
-   Wicket.TreeTable.update(treeTableId);
-
-   // if the update has not been attached to this tree table yet...
-   if (typeof(attached[treeTableId]) == "undefined")
-   {
-      // ... attach it
-      attached[treeTableId] = window.setInterval(function()
-      {
-         Wicket.TreeTable.update(treeTableId);
-      }, 100);
-   }
-};
-

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-core/src/test/java/org/apache/wicket/markup/html/tree/TreeTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/tree/TreeTest.java b/wicket-core/src/test/java/org/apache/wicket/markup/html/tree/TreeTest.java
deleted file mode 100644
index 5e3f414..0000000
--- a/wicket-core/src/test/java/org/apache/wicket/markup/html/tree/TreeTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.markup.html.tree;
-
-import javax.swing.tree.DefaultMutableTreeNode;
-import javax.swing.tree.DefaultTreeModel;
-
-import org.apache.wicket.MarkupContainer;
-import org.apache.wicket.WicketTestCase;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.markup.html.AjaxLink;
-import org.apache.wicket.markup.IMarkupResourceStreamProvider;
-import org.apache.wicket.markup.html.WebPage;
-import org.apache.wicket.util.resource.IResourceStream;
-import org.apache.wicket.util.resource.StringResourceStream;
-import org.junit.Test;
-
-/**
- * @author Pedro Santos
- */
-public class TreeTest extends WicketTestCase
-{
-
-	/**
-	 * Asserting that {@link AbstractTree#treeNodesInserted(javax.swing.event.TreeModelEvent)} adds
-	 * the new item to the dirtyItemsCreateDOM, since there is no parent node at client to be
-	 * recreated.
-	 * 
-	 * @see <a href="https://issues.apache.org/jira/browse/WICKET-3309">WICKET-3309</a>
-	 */
-	@Test
-	public void addChildOnRootAtAnOnRootLessTree()
-	{
-		TestPage testPage = new TestPage();
-		testPage.tree.setRootLess(true);
-		tester.startPage(testPage);
-		tester.clickLink("addToRoot", true);
-		assertTrue(tester.getLastResponseAsString().contains("rootChild"));
-	}
-
-	/**
-	 * Asserting that {@link AbstractTree#treeNodesInserted(javax.swing.event.TreeModelEvent)} don't
-	 * add and not presented node to the AJAX response by invalidating it.
-	 * 
-	 * @see <a href="https://issues.apache.org/jira/browse/WICKET-3309">WICKET-3309</a>
-	 */
-	@Test
-	public void addGrandchildOnRootAtAnRootLessTree()
-	{
-		TestPage testPage = new TestPage();
-		testPage.tree.setRootLess(true);
-		DefaultMutableTreeNode rootChild = new DefaultMutableTreeNode("rootChild");
-		testPage.rootNode.add(rootChild);
-		testPage.tree.getTreeState().selectNode(rootChild, true);
-		tester.startPage(testPage);
-		tester.clickLink("addChildToSelected", true);
-		assertTrue(tester.getLastResponseAsString().contains("newNode"));
-	}
-
-	/**
-	 * Asserting the old leaf root node gets a junction link when adding its first child
-	 * 
-	 * @see <a href="https://issues.apache.org/jira/browse/WICKET-3449">WICKET-3449</a>
-	 */
-	@Test
-	public void junctionLinkRendered()
-	{
-		TestPage testPage = new TestPage();
-		tester.startPage(testPage);
-		tester.clickLink("addToRoot", true);
-		assertTrue(tester.getLastResponseAsString().contains("junctionLink"));
-	}
-
-	/** */
-	public static class TestPage extends WebPage implements IMarkupResourceStreamProvider
-	{
-		private static final long serialVersionUID = 1L;
-
-		AbstractTree tree;
-		DefaultTreeModel treeModel;
-		DefaultMutableTreeNode rootNode;
-
-		/** */
-		public TestPage()
-		{
-			rootNode = new DefaultMutableTreeNode("ROOT");
-			treeModel = new DefaultTreeModel(rootNode);
-			tree = new LinkTree("tree", treeModel);
-			add(tree);
-			add(new AjaxLink<Void>("addToRoot")
-			{
-				private static final long serialVersionUID = 1L;
-
-				@Override
-				public void onClick(AjaxRequestTarget target)
-				{
-					DefaultMutableTreeNode child = new DefaultMutableTreeNode("rootChild");
-					treeModel.insertNodeInto(child, rootNode, rootNode.getChildCount());
-					tree.updateTree(target);
-				}
-			});
-			add(new AjaxLink<Void>("addChildToSelected")
-			{
-				private static final long serialVersionUID = 1L;
-
-				@Override
-				public void onClick(AjaxRequestTarget target)
-				{
-					DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)tree.getTreeState()
-						.getSelectedNodes()
-						.iterator()
-						.next();
-					treeModel.insertNodeInto(new DefaultMutableTreeNode("newNode"), selectedNode,
-						selectedNode.getChildCount());
-					tree.updateTree(target);
-				}
-			});
-		}
-
-		@Override
-		public IResourceStream getMarkupResourceStream(MarkupContainer container,
-			Class<?> containerClass)
-		{
-			return new StringResourceStream("<html><body>" + "<div wicket:id=\"tree\"></div>"
-				+ "<a wicket:id=\"addToRoot\"></a><a wicket:id=\"addChildToSelected\"></a>"
-				+ "</body></html>");
-		}
-
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/PageWindowModel.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/PageWindowModel.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/PageWindowModel.java
index b61a0c0..31f548c 100644
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/PageWindowModel.java
+++ b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/PageWindowModel.java
@@ -22,7 +22,7 @@ import javax.swing.tree.DefaultMutableTreeNode;
 import javax.swing.tree.DefaultTreeModel;
 
 import org.apache.wicket.Application;
-import org.apache.wicket.markup.html.tree.AbstractTree;
+import org.apache.wicket.extensions.markup.html.tree.AbstractTree;
 import org.apache.wicket.model.AbstractReadOnlyModel;
 import org.apache.wicket.pageStore.PageWindowManager.PageWindow;
 import org.apache.wicket.serialize.ISerializer;

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/BaseTreePage.java
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/BaseTreePage.java b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/BaseTreePage.java
index 16eb34d..0a9a324 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/BaseTreePage.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/BaseTreePage.java
@@ -27,7 +27,7 @@ import javax.swing.tree.TreeModel;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.markup.html.AjaxLink;
 import org.apache.wicket.examples.ajax.builtin.BasePage;
-import org.apache.wicket.markup.html.tree.AbstractTree;
+import org.apache.wicket.extensions.markup.html.tree.AbstractTree;
 
 
 /**

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/EditableTreeTablePage.java
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/EditableTreeTablePage.java b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/EditableTreeTablePage.java
index 80a030e..1f9baed 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/EditableTreeTablePage.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/EditableTreeTablePage.java
@@ -16,6 +16,7 @@
  */
 package org.apache.wicket.examples.ajax.builtin.tree;
 
+import org.apache.wicket.extensions.markup.html.tree.AbstractTree;
 import org.apache.wicket.extensions.markup.html.tree.table.ColumnLocation;
 import org.apache.wicket.extensions.markup.html.tree.table.ColumnLocation.Alignment;
 import org.apache.wicket.extensions.markup.html.tree.table.ColumnLocation.Unit;
@@ -23,7 +24,6 @@ import org.apache.wicket.extensions.markup.html.tree.table.IColumn;
 import org.apache.wicket.extensions.markup.html.tree.table.PropertyTreeColumn;
 import org.apache.wicket.extensions.markup.html.tree.table.TreeTable;
 import org.apache.wicket.markup.html.form.Form;
-import org.apache.wicket.markup.html.tree.AbstractTree;
 
 
 /**

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/SimpleTreePage.java
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/SimpleTreePage.java b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/SimpleTreePage.java
index d87d63c..4a2a932 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/SimpleTreePage.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/SimpleTreePage.java
@@ -16,9 +16,9 @@
  */
 package org.apache.wicket.examples.ajax.builtin.tree;
 
-import org.apache.wicket.markup.html.tree.AbstractTree;
-import org.apache.wicket.markup.html.tree.BaseTree;
-import org.apache.wicket.markup.html.tree.LinkTree;
+import org.apache.wicket.extensions.markup.html.tree.AbstractTree;
+import org.apache.wicket.extensions.markup.html.tree.BaseTree;
+import org.apache.wicket.extensions.markup.html.tree.LinkTree;
 
 
 /**

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/TreeTablePage.java
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/TreeTablePage.java b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/TreeTablePage.java
index 0a78534..79f33da 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/TreeTablePage.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/tree/TreeTablePage.java
@@ -16,6 +16,7 @@
  */
 package org.apache.wicket.examples.ajax.builtin.tree;
 
+import org.apache.wicket.extensions.markup.html.tree.AbstractTree;
 import org.apache.wicket.extensions.markup.html.tree.table.ColumnLocation;
 import org.apache.wicket.extensions.markup.html.tree.table.ColumnLocation.Alignment;
 import org.apache.wicket.extensions.markup.html.tree.table.ColumnLocation.Unit;
@@ -23,7 +24,6 @@ import org.apache.wicket.extensions.markup.html.tree.table.IColumn;
 import org.apache.wicket.extensions.markup.html.tree.table.PropertyRenderableColumn;
 import org.apache.wicket.extensions.markup.html.tree.table.PropertyTreeColumn;
 import org.apache.wicket.extensions.markup.html.tree.table.TreeTable;
-import org.apache.wicket.markup.html.tree.AbstractTree;
 
 /**
  * Page that shows a simple tree table.

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-examples/src/main/java/org/apache/wicket/examples/nested/Home.java
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/nested/Home.java b/wicket-examples/src/main/java/org/apache/wicket/examples/nested/Home.java
index bcb871c..54acfab 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/nested/Home.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/nested/Home.java
@@ -26,10 +26,10 @@ import javax.swing.tree.TreeNode;
 
 import org.apache.wicket.examples.WicketExamplePage;
 import org.apache.wicket.examples.ajax.builtin.tree.SimpleTreePage;
+import org.apache.wicket.extensions.markup.html.tree.LinkType;
 import org.apache.wicket.extensions.markup.html.tree.Tree;
 import org.apache.wicket.markup.html.link.BookmarkablePageLink;
 import org.apache.wicket.markup.html.link.Link;
-import org.apache.wicket.markup.html.tree.LinkType;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 
 

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/AbstractTree.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/AbstractTree.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/AbstractTree.java
new file mode 100644
index 0000000..cf75695
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/AbstractTree.java
@@ -0,0 +1,1767 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.extensions.markup.html.tree;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreeNode;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.behavior.Behavior;
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.IMarkupFragment;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.internal.HtmlHeaderContainer;
+import org.apache.wicket.markup.html.list.AbstractItem;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IDetachable;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.request.Response;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.request.resource.ResourceReference;
+import org.apache.wicket.util.lang.Args;
+import org.apache.wicket.util.string.AppendingStringBuffer;
+import org.apache.wicket.util.visit.IVisit;
+import org.apache.wicket.util.visit.IVisitor;
+
+
+/**
+ * This class encapsulates the logic for displaying and (partial) updating the tree. Actual
+ * presentation is out of scope of this class. User should derive they own tree (if needed) from
+ * {@link BaseTree} (recommended).
+ * 
+ * @author Matej Knopp
+ */
+@Deprecated
+public abstract class AbstractTree extends Panel
+	implements
+		ITreeStateListener,
+		TreeModelListener,
+		AjaxRequestTarget.ITargetRespondListener
+{
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Interface for visiting individual tree items.
+	 */
+	private static interface IItemCallback
+	{
+		/**
+		 * Visits the tree item.
+		 * 
+		 * @param item
+		 *            the item to visit
+		 */
+		void visitItem(TreeItem item);
+	}
+
+	/**
+	 * This class represents one row in rendered tree (TreeNode). Only TreeNodes that are visible
+	 * (all their parent are expanded) have TreeItem created for them.
+	 */
+	private final class TreeItem extends AbstractItem
+	{
+		/**
+		 * whether this tree item should also render it's children to response. this is set if we
+		 * need the whole subtree rendered as one component in ajax response, so that we can replace
+		 * it in one step (replacing individual rows is very slow in javascript, therefore we
+		 * replace the whole subtree)
+		 */
+		private final static int FLAG_RENDER_CHILDREN = FLAG_RESERVED8;
+
+		private static final long serialVersionUID = 1L;
+
+		/**
+		 * tree item children - we need this to traverse items in correct order when rendering
+		 */
+		private List<TreeItem> children = null;
+
+		/** tree item level - how deep is this item in tree */
+		private final int level;
+
+		private final TreeItem parent;
+
+		/**
+		 * Construct.
+		 * 
+		 * @param id
+		 *            The component id
+		 * @param node
+		 *            tree node
+		 * @param level
+		 *            current level
+		 * @param parent
+		 */
+		public TreeItem(TreeItem parent, String id, final Object node, int level)
+		{
+			super(id, new Model<Serializable>((Serializable)node));
+
+			this.parent = parent;
+
+			nodeToItemMap.put(node, this);
+			this.level = level;
+			setOutputMarkupId(true);
+
+			// if this isn't a root item in rootless mode
+			if (level != -1)
+			{
+				populateTreeItem(this, level);
+			}
+		}
+
+		public TreeItem getParentItem()
+		{
+			return parent;
+		}
+
+		/**
+		 * @return The children
+		 */
+		public List<TreeItem> getChildren()
+		{
+			return children;
+		}
+
+		/**
+		 * @return The current level
+		 */
+		public int getLevel()
+		{
+			return level;
+		}
+
+		/**
+		 * @see org.apache.wicket.Component#getMarkupId()
+		 */
+		@Override
+		public String getMarkupId()
+		{
+			// this is overridden to produce id that begins with id of tree
+			// if the tree has set (shorter) id in markup, we can use it to
+			// shorten the id of individual TreeItems
+			return AbstractTree.this.getMarkupId() + "_" + getId();
+		}
+
+		/**
+		 * Sets the children.
+		 * 
+		 * @param children
+		 *            The children
+		 */
+		public void setChildren(List<TreeItem> children)
+		{
+			this.children = children;
+		}
+
+		/**
+		 * Whether to render children.
+		 * 
+		 * @return whether to render children
+		 */
+		protected final boolean isRenderChildren()
+		{
+			return getFlag(FLAG_RENDER_CHILDREN);
+		}
+
+		/**
+		 * Whether the TreeItem has any child TreeItems
+		 * 
+		 * @return true if there are one or more child TreeItems; false otherwise
+		 */
+		public boolean hasChildTreeItems()
+		{
+			return children != null && !children.isEmpty();
+		}
+
+		/**
+		 * @see org.apache.wicket.MarkupContainer#onRender()
+		 */
+		@Override
+		protected void onRender()
+		{
+			// is this root and tree is in rootless mode?
+			if (this == rootItem && isRootLess() == true)
+			{
+				// yes, write empty div with id
+				// this is necessary for createElement js to work correctly
+				String tagName = ((ComponentTag)getMarkup().get(0)).getName();
+				Response response = getResponse();
+				response.write("<" + tagName + " style=\"display:none\" id=\"" + getMarkupId() +
+					"\">");
+				if ("table".equals(tagName))
+				{
+					response.write("<tbody><tr><td></td></tr></tbody>");
+				}
+				response.write("</" + tagName + ">");
+			}
+			else
+			{
+				// render the item
+				super.onRender();
+
+				// should we also render children (ajax response)
+				if (isRenderChildren())
+				{
+					// visit every child
+					visitItemChildren(this, new IItemCallback()
+					{
+						@Override
+						public void visitItem(TreeItem item)
+						{
+							// render child
+							item.onRender();
+
+							// go through the behaviors and invoke IBehavior.afterRender
+							List<? extends Behavior> behaviors = item.getBehaviors();
+							for (Behavior behavior : behaviors)
+							{
+								behavior.afterRender(item);
+							}
+						}
+					});
+				}
+			}
+		}
+
+		/**
+		 * 
+		 * @return model object
+		 */
+		public Object getModelObject()
+		{
+			return getDefaultModelObject();
+		}
+
+		@Override
+		public void renderHead(final HtmlHeaderContainer container)
+		{
+			super.renderHead(container);
+
+			if (isRenderChildren())
+			{
+				// visit every child
+				visitItemChildren(this, new IItemCallback()
+				{
+					@Override
+					public void visitItem(TreeItem item)
+					{
+						// write header contributions from the children of item
+						item.visitChildren(new IVisitor<Component, Void>()
+						{
+							@Override
+							public void component(final Component component,
+								final IVisit<Void> visit)
+							{
+								if (component.isVisible())
+								{
+									component.renderHead(container);
+								}
+								else
+								{
+									visit.dontGoDeeper();
+								}
+							}
+						});
+					}
+				});
+			}
+		}
+
+		protected final void setRenderChildren(boolean value)
+		{
+			setFlag(FLAG_RENDER_CHILDREN, value);
+		}
+
+		@Override
+		protected void onDetach()
+		{
+			super.onDetach();
+			Object object = getModelObject();
+			if (object instanceof IDetachable)
+			{
+				((IDetachable)object).detach();
+			}
+
+			if (isRenderChildren())
+			{
+				// visit every child
+				visitItemChildren(this, new IItemCallback()
+				{
+					@Override
+					public void visitItem(TreeItem item)
+					{
+						item.detach();
+					}
+				});
+			}
+
+			// children are rendered, clear the flag
+			setRenderChildren(false);
+		}
+
+		@Override
+		protected void onBeforeRender()
+		{
+			onBeforeRenderInternal();
+			super.onBeforeRender();
+
+			if (isRenderChildren())
+			{
+				// visit every child
+				visitItemChildren(this, new IItemCallback()
+				{
+					@Override
+					public void visitItem(TreeItem item)
+					{
+						item.prepareForRender();
+					}
+				});
+			}
+		}
+
+		@Override
+		protected void onAfterRender()
+		{
+			super.onAfterRender();
+			if (isRenderChildren())
+			{
+				// visit every child
+				visitItemChildren(this, new IItemCallback()
+				{
+					@Override
+					public void visitItem(TreeItem item)
+					{
+						item.afterRender();
+					}
+				});
+			}
+		}
+
+		private boolean hasParentWithChildrenMarkedToRecreation()
+		{
+			return getParentItem() != null &&
+				(getParentItem().getChildren() == null || getParentItem().hasParentWithChildrenMarkedToRecreation());
+		}
+	}
+
+	/**
+	 * Components that holds tree items. This is similar to ListView, but it renders tree items in
+	 * the right order.
+	 */
+	private class TreeItemContainer extends WebMarkupContainer
+	{
+		private static final long serialVersionUID = 1L;
+
+		/**
+		 * Construct.
+		 * 
+		 * @param id
+		 *            The component id
+		 */
+		public TreeItemContainer(String id)
+		{
+			super(id);
+		}
+
+		/**
+		 * @see org.apache.wicket.MarkupContainer#remove(org.apache.wicket.Component)
+		 */
+		@Override
+		public TreeItemContainer remove(Component component)
+		{
+			// when a treeItem is removed, remove reference to it from
+			// nodeToItemMAp
+			if (component instanceof TreeItem)
+			{
+				nodeToItemMap.remove(((TreeItem)component).getModelObject());
+			}
+			super.remove(component);
+			return this;
+		}
+
+		/**
+		 * @see org.apache.wicket.MarkupContainer#onRender()
+		 */
+		@Override
+		protected void onRender()
+		{
+			// is there a root item? (non-empty tree)
+			if (rootItem != null)
+			{
+				IItemCallback callback = new IItemCallback()
+				{
+					@Override
+					public void visitItem(TreeItem item)
+					{
+						// render component
+						item.render();
+					}
+				};
+
+				// visit item and it's children
+				visitItemAndChildren(rootItem, callback);
+			}
+		}
+
+		@Override
+		public IMarkupFragment getMarkup(final Component child)
+		{
+			// The childs markup is always equal to the parents markup.
+			return getMarkup();
+		}
+	}
+
+	private boolean attached = false;
+
+	/** comma separated list of ids of elements to be deleted. */
+	private final AppendingStringBuffer deleteIds = new AppendingStringBuffer();
+
+	/**
+	 * whether the whole tree is dirty (so the whole tree needs to be refreshed).
+	 */
+	private boolean dirtyAll = false;
+
+	/**
+	 * list of dirty items. if children property of these items is null, the children will be
+	 * rebuild.
+	 */
+	private final Set<TreeItem> dirtyItems = new HashSet<TreeItem>();
+
+	/**
+	 * list of dirty items which need the DOM structure to be created for them (added items)
+	 */
+	private final Set<TreeItem> dirtyItemsCreateDOM = new HashSet<TreeItem>();
+
+	/** counter for generating unique ids of every tree item. */
+	private int idCounter = 0;
+
+	/** Component whose children are tree items. */
+	private TreeItemContainer itemContainer;
+
+	/**
+	 * map that maps TreeNode to TreeItem. TreeItems only exists for TreeNodes, that are visible
+	 * (their parents are not collapsed).
+	 */
+	// TODO this field is not serializable but nested inside an serializable component
+	private final Map<Object, TreeItem> nodeToItemMap = new HashMap<Object, TreeItem>();
+
+	/**
+	 * we need to track previous model. if the model changes, we unregister the tree from listeners
+	 * of old model and register the tree as listener of new model.
+	 */
+
+	// TODO this field is not serializable but nested inside an serializable component
+	private TreeModel previousModel = null;
+
+	/** root item of the tree. */
+	private TreeItem rootItem = null;
+
+	/** whether the tree root is shown. */
+	private boolean rootLess = false;
+
+	/** stores reference to tree state. */
+	private ITreeState state;
+
+	/**
+	 * Tree constructor
+	 * 
+	 * @param id
+	 *            The component id
+	 */
+	public AbstractTree(String id)
+	{
+		super(id);
+		init();
+	}
+
+	/**
+	 * Tree constructor
+	 * 
+	 * @param id
+	 *            The component id
+	 * @param model
+	 *            The tree model
+	 */
+	public AbstractTree(String id, IModel<? extends TreeModel> model)
+	{
+		super(id, model);
+		init();
+	}
+
+	/** called when all nodes are collapsed. */
+	@Override
+	public final void allNodesCollapsed()
+	{
+		invalidateAll();
+	}
+
+	/** called when all nodes are expanded. */
+	@Override
+	public final void allNodesExpanded()
+	{
+		invalidateAll();
+	}
+
+	/**
+	 * 
+	 * @return model
+	 */
+	@SuppressWarnings("unchecked")
+	public IModel<? extends TreeModel> getModel()
+	{
+		return (IModel<? extends TreeModel>)getDefaultModel();
+	}
+
+	/**
+	 * @return treemodel
+	 */
+	public TreeModel getModelObject()
+	{
+		return (TreeModel)getDefaultModelObject();
+	}
+
+	/**
+	 * 
+	 * @param model
+	 * @return this
+	 */
+	public MarkupContainer setModel(IModel<? extends TreeModel> model)
+	{
+		setDefaultModel(model);
+		return this;
+	}
+
+	/**
+	 * 
+	 * @param model
+	 * @return this
+	 */
+	public MarkupContainer setModelObject(TreeModel model)
+	{
+		setDefaultModelObject(model);
+		return this;
+	}
+
+	/**
+	 * Returns the TreeState of this tree.
+	 * 
+	 * @return Tree state instance
+	 */
+	public ITreeState getTreeState()
+	{
+		if (state == null)
+		{
+			state = newTreeState();
+
+			// add this object as listener of the state
+			state.addTreeStateListener(this);
+			// FIXME: Where should we remove the listener?
+		}
+		return state;
+	}
+
+	/**
+	 * This method is called before the onAttach is called. Code here gets executed before the items
+	 * have been populated.
+	 */
+	protected void onBeforeAttach()
+	{
+	}
+
+	// This is necessary because MarkupContainer.onBeforeRender involves calling
+	// beforeRender on children, which results in stack overflow when called from TreeItem
+	private void onBeforeRenderInternal()
+	{
+		if (attached == false)
+		{
+			onBeforeAttach();
+
+			checkModel();
+
+			// Do we have to rebuild the whole tree?
+			if (dirtyAll && rootItem != null)
+			{
+				clearAllItem();
+			}
+			else
+			{
+				// rebuild children of dirty nodes that need it
+				rebuildDirty();
+			}
+
+			// is root item created? (root item is null if the items have not
+			// been created yet, or the whole tree was dirty and clearAllITem
+			// has been called
+			if (rootItem == null)
+			{
+				Object rootNode = getModelObject().getRoot();
+				if (rootNode != null)
+				{
+					if (isRootLess())
+					{
+						rootItem = newTreeItem(null, rootNode, -1);
+					}
+					else
+					{
+						rootItem = newTreeItem(null, rootNode, 0);
+					}
+					itemContainer.add(rootItem);
+					buildItemChildren(rootItem);
+				}
+			}
+
+			attached = true;
+		}
+	}
+
+	/**
+	 * Called at the beginning of the request (not ajax request, unless we are rendering the entire
+	 * component)
+	 */
+	@Override
+	public void onBeforeRender()
+	{
+		onBeforeRenderInternal();
+		super.onBeforeRender();
+	}
+
+	/**
+	 * @see org.apache.wicket.MarkupContainer#onDetach()
+	 */
+	@Override
+	public void onDetach()
+	{
+		attached = false;
+		super.onDetach();
+		if (getTreeState() instanceof IDetachable)
+		{
+			((IDetachable)getTreeState()).detach();
+		}
+	}
+
+	/**
+	 * Call to refresh the whole tree. This should only be called when the roodNode has been
+	 * replaced or the entiry tree model changed.
+	 */
+	public final void invalidateAll()
+	{
+		updated();
+		dirtyAll = true;
+	}
+
+	/**
+	 * @return whether the tree root is shown
+	 */
+	public final boolean isRootLess()
+	{
+		return rootLess;
+	}
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.ITreeStateListener#nodeCollapsed(Object)
+	 */
+	@Override
+	public final void nodeCollapsed(Object node)
+	{
+		if (isNodeVisible(node) == true)
+		{
+			invalidateNodeWithChildren(node);
+		}
+	}
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.ITreeStateListener#nodeExpanded(Object)
+	 */
+	@Override
+	public final void nodeExpanded(Object node)
+	{
+		if (isNodeVisible(node) == true)
+		{
+			invalidateNodeWithChildren(node);
+		}
+	}
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.ITreeStateListener#nodeSelected(Object)
+	 */
+	@Override
+	public final void nodeSelected(Object node)
+	{
+		if (isNodeVisible(node))
+		{
+			invalidateNode(node, isForceRebuildOnSelectionChange());
+		}
+	}
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.ITreeStateListener#nodeUnselected(Object)
+	 */
+	@Override
+	public final void nodeUnselected(Object node)
+	{
+		if (isNodeVisible(node))
+		{
+			invalidateNode(node, isForceRebuildOnSelectionChange());
+		}
+	}
+
+	/**
+	 * Determines whether the TreeNode needs to be rebuilt if it is selected or deselected
+	 * 
+	 * @return true if the node should be rebuilt after (de)selection, false otherwise
+	 */
+	protected boolean isForceRebuildOnSelectionChange()
+	{
+		return true;
+	}
+
+	/**
+	 * Sets whether the root of the tree should be visible.
+	 * 
+	 * @param rootLess
+	 *            whether the root should be visible
+	 */
+	public void setRootLess(boolean rootLess)
+	{
+		if (this.rootLess != rootLess)
+		{
+			this.rootLess = rootLess;
+			invalidateAll();
+
+			// if the tree is in rootless mode, make sure the root node is
+			// expanded
+			if (rootLess == true && getModelObject() != null)
+			{
+				getTreeState().expandNode(getModelObject().getRoot());
+			}
+		}
+	}
+
+	/**
+	 * @see javax.swing.event.TreeModelListener#treeNodesChanged(javax.swing.event.TreeModelEvent)
+	 */
+	@Override
+	public final void treeNodesChanged(TreeModelEvent e)
+	{
+		if (dirtyAll)
+		{
+			return;
+		}
+		// has root node changed?
+		if (e.getChildren() == null)
+		{
+			if (rootItem != null)
+			{
+				invalidateNode(rootItem.getModelObject(), true);
+			}
+		}
+		else
+		{
+			// go through all changed nodes
+			Object[] children = e.getChildren();
+			if (children != null)
+			{
+				for (Object node : children)
+				{
+					if (isNodeVisible(node))
+					{
+						// if the nodes is visible invalidate it
+						invalidateNode(node, true);
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Marks the last but one visible child node of the given item as dirty, if give child is the
+	 * last item of parent.
+	 * 
+	 * We need this to refresh the previous visible item in case the inserted / deleted item was
+	 * last. The reason is that the line shape of previous item changes from L to |- .
+	 * 
+	 * @param parent
+	 * @param child
+	 */
+	private void markTheLastButOneChildDirty(TreeItem parent, TreeItem child)
+	{
+		if (parent.getChildren().indexOf(child) == parent.getChildren().size() - 1)
+		{
+			// go through the children backwards, start at the last but one
+			// item
+			for (int i = parent.getChildren().size() - 2; i >= 0; --i)
+			{
+				TreeItem item = parent.getChildren().get(i);
+
+				// invalidate the node and it's children, so that they are
+				// redrawn
+				invalidateNodeWithChildren(item.getModelObject());
+
+			}
+		}
+	}
+
+	/**
+	 * @see javax.swing.event.TreeModelListener#treeNodesInserted(javax.swing.event.TreeModelEvent)
+	 */
+	@Override
+	public final void treeNodesInserted(TreeModelEvent e)
+	{
+		if (dirtyAll)
+		{
+			return;
+		}
+
+		// get the parent node of inserted nodes
+		Object parentNode = e.getTreePath().getLastPathComponent();
+		TreeItem parentItem = nodeToItemMap.get(parentNode);
+
+
+		if (parentItem != null && isNodeVisible(parentNode))
+		{
+			List<?> eventChildren = Arrays.asList(e.getChildren());
+
+			// parentNode was a leaf before this insertion event only if every one of
+			// its current children is in the event's list of children
+			boolean wasLeaf = true;
+			int nodeChildCount = getChildCount(parentNode);
+			for (int i = 0; wasLeaf && i < nodeChildCount; i++)
+			{
+				wasLeaf = eventChildren.contains(getChildAt(parentNode, i));
+			}
+
+			boolean addingToHiddedRoot = parentItem.getParentItem() == null && isRootLess();
+			// if parent was a presented leaf
+			if (wasLeaf && !addingToHiddedRoot)
+			{
+				// parentNode now has children for the first time, so we may need to invalidate
+				// grandparent so that parentNode's junctionLink gets rebuilt with a plus/minus link
+				Object grandparentNode = getParentNode(parentNode);
+				boolean addingToHiddedRootSon = grandparentNode != null &&
+					getParentNode(grandparentNode) == null && isRootLess();
+				// if visible, invalidate the grandparent
+				if (grandparentNode != null && !addingToHiddedRootSon)
+				{
+					invalidateNodeWithChildren(grandparentNode);
+				}
+				else
+				{
+					// if not, simply invalidating the parent node
+					// OBS.: forcing rebuild since unlike the grandparent, the old
+					// leaf parent needs to rebuild with plus/minus link
+					invalidateNode(parentNode, true);
+				}
+				getTreeState().expandNode(parentNode);
+			}
+			else
+			{
+				if (isNodeExpanded(parentNode))
+				{
+					List<TreeItem> itemChildren = parentItem.getChildren();
+					int childLevel = parentItem.getLevel() + 1;
+					final int[] childIndices = e.getChildIndices();
+					for (int i = 0; i < eventChildren.size(); ++i)
+					{
+						TreeItem item = newTreeItem(parentItem, eventChildren.get(i), childLevel);
+						itemContainer.add(item);
+
+						if (itemChildren != null)
+						{
+							itemChildren.add(childIndices[i], item);
+							markTheLastButOneChildDirty(parentItem, item);
+						}
+
+						if (!dirtyItems.contains(item))
+						{
+							dirtyItems.add(item);
+						}
+
+						if (!dirtyItemsCreateDOM.contains(item) &&
+							!item.hasParentWithChildrenMarkedToRecreation())
+						{
+							dirtyItemsCreateDOM.add(item);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * @see javax.swing.event.TreeModelListener#treeNodesRemoved(javax.swing.event.TreeModelEvent)
+	 */
+	@Override
+	public final void treeNodesRemoved(TreeModelEvent removalEvent)
+	{
+		if (dirtyAll)
+		{
+			return;
+		}
+
+		// get the parent node of deleted nodes
+		Object parentNode = removalEvent.getTreePath().getLastPathComponent();
+		TreeItem parentItem = nodeToItemMap.get(parentNode);
+
+		// unselect all removed items
+		List<Object> selection = new ArrayList<Object>(getTreeState().getSelectedNodes());
+		List<Object> removed = Arrays.asList(removalEvent.getChildren());
+		for (Object selectedNode : selection)
+		{
+			Object cursor = selectedNode;
+			while (cursor != null)
+			{
+				if (removed.contains(cursor))
+				{
+					getTreeState().selectNode(selectedNode, false);
+				}
+				if (cursor instanceof TreeNode)
+				{
+					cursor = ((TreeNode)cursor).getParent();
+				}
+				else
+				{
+					cursor = null;
+				}
+			}
+		}
+
+		if (parentItem != null && isNodeVisible(parentNode))
+		{
+			if (isNodeExpanded(parentNode))
+			{
+				// deleted nodes were visible; we need to delete their TreeItems
+				for (Object deletedNode : removalEvent.getChildren())
+				{
+					TreeItem itemToDelete = nodeToItemMap.get(deletedNode);
+					if (itemToDelete != null)
+					{
+						markTheLastButOneChildDirty(parentItem, itemToDelete);
+
+						// remove all the deleted item's children
+						visitItemChildren(itemToDelete, new IItemCallback()
+						{
+							@Override
+							public void visitItem(TreeItem item)
+							{
+								removeItem(item);
+							}
+						});
+
+						parentItem.getChildren().remove(itemToDelete);
+						removeItem(itemToDelete);
+					}
+				}
+			}
+
+			if (!parentItem.hasChildTreeItems())
+			{
+				// rebuild parent's icon to show it no longer has children
+				invalidateNode(parentNode, true);
+			}
+		}
+	}
+
+	/**
+	 * @see javax.swing.event.TreeModelListener#treeStructureChanged(javax.swing.event.TreeModelEvent)
+	 */
+	@Override
+	public final void treeStructureChanged(TreeModelEvent e)
+	{
+		if (dirtyAll)
+		{
+			return;
+		}
+
+		// get the parent node of changed nodes
+		Object node = e.getTreePath() != null ? e.getTreePath().getLastPathComponent() : null;
+
+		// has the tree root changed?
+		if (node == null || e.getTreePath().getPathCount() == 1)
+		{
+			invalidateAll();
+		}
+		else
+		{
+			invalidateNodeWithChildren(node);
+		}
+	}
+
+	/**
+	 * Allows to intercept adding dirty components to AjaxRequestTarget.
+	 * 
+	 * @param target
+	 * @param component
+	 */
+	protected void addComponent(AjaxRequestTarget target, Component component)
+	{
+		target.add(component);
+	}
+
+	@Override
+	public void onTargetRespond(AjaxRequestTarget target)
+	{
+		// check whether the model hasn't changed
+		checkModel();
+
+		// is the whole tree dirty
+		if (dirtyAll)
+		{
+			// render entire tree component
+			addComponent(target, this);
+		}
+		else
+		{
+			// remove DOM elements that need to be removed
+			if (deleteIds.length() != 0)
+			{
+				String js = getElementsDeleteJavaScript();
+
+				// add the javascript to target
+				target.prependJavaScript(js);
+			}
+
+			// We have to repeat this as long as there are any dirty items to be
+			// created.
+			// The reason why we can't do this in one pass is that some of the
+			// items
+			// may need to be inserted after items that has not been inserted
+			// yet, so we have
+			// to detect those and wait until the items they depend on are
+			// inserted.
+			while (dirtyItemsCreateDOM.isEmpty() == false)
+			{
+				for (Iterator<TreeItem> i = dirtyItemsCreateDOM.iterator(); i.hasNext();)
+				{
+					TreeItem item = i.next();
+					TreeItem parent = item.getParentItem();
+					int index = parent.getChildren().indexOf(item);
+					TreeItem previous;
+					// we need item before this (in dom structure)
+
+					if (index == 0)
+					{
+						previous = parent;
+					}
+					else
+					{
+						previous = parent.getChildren().get(index - 1);
+						// get the last item of previous item subtree
+						while (previous.getChildren() != null && previous.getChildren().size() > 0)
+						{
+							previous = previous.getChildren()
+								.get(previous.getChildren().size() - 1);
+						}
+					}
+					// check if the previous item isn't waiting to be inserted
+					if (dirtyItemsCreateDOM.contains(previous) == false)
+					{
+						// it's already in dom, so we can use it as point of
+						// insertion
+						target.prependJavaScript("Wicket.Tree.createElement(\"" +
+							item.getMarkupId() + "\"," + "\"" + previous.getMarkupId() + "\")");
+
+						// remove the item so we don't process it again
+						i.remove();
+					}
+					else
+					{
+						// we don't do anything here, inserting this item will
+						// have to wait
+						// until the previous item gets inserted
+					}
+				}
+			}
+
+			// iterate through dirty items
+			for (TreeItem item : dirtyItems)
+			{
+				// does the item need to rebuild children?
+				if (item.getChildren() == null)
+				{
+					// rebuild the children
+					buildItemChildren(item);
+
+					// set flag on item so that it renders itself together with
+					// it's children
+					item.setRenderChildren(true);
+				}
+
+				// add the component to target
+				addComponent(target, item);
+			}
+
+			// clear dirty flags
+			updated();
+		}
+	}
+
+	/**
+	 * Convenience method that updates changed portions on tree. You can call this method during
+	 * Ajax response, where calling {@link #updateTree(AjaxRequestTarget)} would be appropriate, but
+	 * you don't have the AjaxRequestTarget instance. However, it is also safe to call this method
+	 * outside Ajax response.
+	 */
+	public final void updateTree()
+	{
+		AjaxRequestTarget handler = AjaxRequestTarget.get();
+		if (handler == null)
+		{
+			throw new WicketRuntimeException(
+				"No AjaxRequestTarget available to execute updateTree(ART target)");
+		}
+
+		updateTree(handler);
+	}
+
+	/**
+	 * Updates the changed portions of the tree using given AjaxRequestTarget. Call this method if
+	 * you modified the tree model during an ajax request target and you want to partially update
+	 * the component on page. Make sure that the tree model has fired the proper listener functions.
+	 * <p>
+	 * <b>You can only call this method once in a request.</b>
+	 * 
+	 * @param target
+	 *            Ajax request target used to send the update to the page
+	 */
+	public final void updateTree(final AjaxRequestTarget target)
+	{
+		Args.notNull(target, "target");
+		target.registerRespondListener(this);
+	}
+
+	/**
+	 * Returns whether the given node is expanded.
+	 * 
+	 * @param node
+	 *            The node to inspect
+	 * @return true if the node is expanded, false otherwise
+	 */
+	protected final boolean isNodeExpanded(Object node)
+	{
+		// In root less mode the root node is always expanded
+		if (isRootLess() && rootItem != null && rootItem.getModelObject().equals(node))
+		{
+			return true;
+		}
+
+		return getTreeState().isNodeExpanded(node);
+	}
+
+	/**
+	 * Creates the TreeState, which is an object where the current state of tree (which nodes are
+	 * expanded / collapsed, selected, ...) is stored.
+	 * 
+	 * @return Tree state instance
+	 */
+	protected ITreeState newTreeState()
+	{
+		return new DefaultTreeState();
+	}
+
+	/**
+	 * Called after the rendering of tree is complete. Here we clear the dirty flags.
+	 */
+	@Override
+	protected void onAfterRender()
+	{
+		super.onAfterRender();
+		// rendering is complete, clear all dirty flags and items
+		updated();
+	}
+
+	/**
+	 * This method is called after creating every TreeItem. This is the place for adding components
+	 * on item (junction links, labels, icons...)
+	 * 
+	 * @param item
+	 *            newly created tree item. The node can be obtained as item.getModelObject()
+	 * 
+	 * @param level
+	 *            how deep the component is in tree hierarchy (0 for root item)
+	 */
+	protected abstract void populateTreeItem(WebMarkupContainer item, int level);
+
+	/**
+	 * Builds the children for given TreeItem. It recursively traverses children of it's TreeNode
+	 * and creates TreeItem for every visible TreeNode.
+	 * 
+	 * @param item
+	 *            The parent tree item
+	 */
+	private void buildItemChildren(TreeItem item)
+	{
+		List<TreeItem> items;
+
+		// if the node is expanded
+		if (isNodeExpanded(item.getModelObject()))
+		{
+			// build the items for children of the items' treenode.
+			items = buildTreeItems(item, nodeChildren(item.getModelObject()), item.getLevel() + 1);
+		}
+		else
+		{
+			// it's not expanded, just set children to an empty list
+			items = new ArrayList<TreeItem>(0);
+		}
+
+		item.setChildren(items);
+	}
+
+	/**
+	 * Builds (recursively) TreeItems for the given Iterator of TreeNodes.
+	 * 
+	 * @param parent
+	 * @param nodes
+	 *            The nodes to build tree items for
+	 * @param level
+	 *            The current level
+	 * @return List with new tree items
+	 */
+	private List<TreeItem> buildTreeItems(TreeItem parent, Iterator<Object> nodes, int level)
+	{
+		List<TreeItem> result = new ArrayList<TreeItem>();
+
+		// for each node
+		while (nodes.hasNext())
+		{
+			Object node = nodes.next();
+			// create tree item
+			TreeItem item = newTreeItem(parent, node, level);
+			itemContainer.add(item);
+
+			// builds it children (recursively)
+			buildItemChildren(item);
+
+			// add item to result
+			result.add(item);
+		}
+
+		return result;
+	}
+
+	/**
+	 * Checks whether the model has been changed, and if so unregister and register listeners.
+	 */
+	private void checkModel()
+	{
+		// find out whether the model object (the TreeModel) has been changed
+		TreeModel model = getModelObject();
+		if (model != previousModel)
+		{
+			if (previousModel != null)
+			{
+				previousModel.removeTreeModelListener(this);
+			}
+
+			previousModel = model;
+
+			if (model != null)
+			{
+				model.addTreeModelListener(this);
+			}
+			// model has been changed, redraw whole tree
+			invalidateAll();
+		}
+	}
+
+	/**
+	 * Removes all TreeItem components.
+	 */
+	private void clearAllItem()
+	{
+		visitItemAndChildren(rootItem, new IItemCallback()
+		{
+			@Override
+			public void visitItem(TreeItem item)
+			{
+				item.remove();
+			}
+		});
+		rootItem = null;
+	}
+
+	/**
+	 * Returns the javascript used to delete removed elements.
+	 * 
+	 * @return The javascript
+	 */
+	private String getElementsDeleteJavaScript()
+	{
+		// build the javascript call
+		final AppendingStringBuffer buffer = new AppendingStringBuffer(100);
+
+		buffer.append("Wicket.Tree.removeNodes(\"");
+
+		// first parameter is the markup id of tree (will be used as prefix to
+		// build ids of child items
+		buffer.append(getMarkupId() + "_\",[");
+
+		// append the ids of elements to be deleted
+		buffer.append(deleteIds);
+
+		// does the buffer end if ','?
+		if (buffer.endsWith(","))
+		{
+			// it does, trim it
+			buffer.setLength(buffer.length() - 1);
+		}
+
+		buffer.append("]);");
+
+		return buffer.toString();
+	}
+
+	//
+	// State and Model callbacks
+	//
+
+	/**
+	 * returns the short version of item id (just the number part).
+	 * 
+	 * @param item
+	 *            The tree item
+	 * @return The id
+	 */
+	private String getShortItemId(TreeItem item)
+	{
+		// show much of component id can we skip? (to minimize the length of
+		// javascript being sent)
+		final int skip = getMarkupId().length() + 1; // the length of id of
+		// tree and '_'.
+		return item.getMarkupId().substring(skip);
+	}
+
+	private final static ResourceReference JAVASCRIPT = new JavaScriptResourceReference(
+		AbstractTree.class, "res/tree.js");
+
+	/**
+	 * Initialize the component.
+	 */
+	private void init()
+	{
+		setVersioned(false);
+
+		// we need id when we are replacing the whole tree
+		setOutputMarkupId(true);
+
+		// create container for tree items
+		itemContainer = new TreeItemContainer("i");
+		add(itemContainer);
+
+		checkModel();
+	}
+
+	/**
+	 * INTERNAL
+	 * 
+	 * @param node
+	 */
+	public final void markNodeDirty(Object node)
+	{
+		invalidateNode(node, false);
+	}
+
+	/**
+	 * INTERNAL
+	 * 
+	 * @param node
+	 */
+	public final void markNodeChildrenDirty(Object node)
+	{
+		TreeItem item = nodeToItemMap.get(node);
+		if (item != null)
+		{
+			visitItemChildren(item, new IItemCallback()
+			{
+				@Override
+				public void visitItem(TreeItem item)
+				{
+					invalidateNode(item.getModelObject(), false);
+				}
+			});
+		}
+	}
+
+	/**
+	 * Invalidates single node (without children). On the next render, this node will be updated.
+	 * Node will not be rebuilt, unless forceRebuild is true.
+	 * 
+	 * @param node
+	 *            The node to invalidate
+	 * @param forceRebuild
+	 */
+	private void invalidateNode(Object node, boolean forceRebuild)
+	{
+		if (dirtyAll == false)
+		{
+			// get item for this node
+			TreeItem item = nodeToItemMap.get(node);
+
+			if (item != null)
+			{
+				boolean createDOM = false;
+
+				if (forceRebuild)
+				{
+					// recreate the item
+					int level = item.getLevel();
+					List<TreeItem> children = item.getChildren();
+					String id = item.getId();
+
+					// store the parent of old item
+					TreeItem parent = item.getParentItem();
+
+					// if the old item has a parent, store it's index
+					int index = parent != null ? parent.getChildren().indexOf(item) : -1;
+
+					createDOM = dirtyItemsCreateDOM.contains(item);
+
+					dirtyItems.remove(item);
+					dirtyItemsCreateDOM.remove(item);
+
+					item.remove();
+
+					item = newTreeItem(parent, node, level, id);
+					itemContainer.add(item);
+
+					item.setChildren(children);
+
+					// was the item an root item?
+					if (parent == null)
+					{
+						rootItem = item;
+					}
+					else
+					{
+						parent.getChildren().set(index, item);
+					}
+				}
+
+				if (!dirtyItems.contains(item))
+				{
+					dirtyItems.add(item);
+				}
+
+				if (createDOM && !dirtyItemsCreateDOM.contains(item))
+				{
+					dirtyItemsCreateDOM.add(item);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Invalidates node and it's children. On the next render, the node and children will be
+	 * updated. Node children will be rebuilt.
+	 * 
+	 * @param node
+	 *            The node to invalidate
+	 */
+	private void invalidateNodeWithChildren(Object node)
+	{
+		if (dirtyAll == false)
+		{
+			// get item for this node
+			TreeItem item = nodeToItemMap.get(node);
+
+			// is the item visible?
+			if (item != null)
+			{
+				// go though item children and remove every one of them
+				visitItemChildren(item, new IItemCallback()
+				{
+					@Override
+					public void visitItem(TreeItem item)
+					{
+						removeItem(item);
+					}
+				});
+
+				// set children to null so that they get rebuild
+				item.setChildren(null);
+
+				if (!dirtyItems.contains(item))
+				{
+					// add item to dirty items
+					dirtyItems.add(item);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Returns whether the given node is visible, e.g. all it's parents are expanded.
+	 * 
+	 * @param node
+	 *            The node to inspect
+	 * @return true if the node is visible, false otherwise
+	 */
+	private boolean isNodeVisible(Object node)
+	{
+		if (node == null)
+		{
+			return false;
+		}
+		Object parent = getParentNode(node);
+		while (parent != null)
+		{
+			if (isNodeExpanded(parent) == false)
+			{
+				return false;
+			}
+			parent = getParentNode(parent);
+		}
+		return true;
+	}
+
+	/**
+	 * Returns parent node of given node.
+	 * 
+	 * @param node
+	 * @return parent node
+	 */
+	public Object getParentNode(Object node)
+	{
+		TreeItem item = nodeToItemMap.get(node);
+		if (item == null)
+		{
+			return null;
+		}
+		else
+		{
+			TreeItem parent = item.getParentItem();
+			return parent == null ? null : parent.getModelObject();
+		}
+	}
+
+	/**
+	 * Creates a tree item for given node.
+	 * 
+	 * @param parent
+	 * @param node
+	 *            The tree node
+	 * @param level
+	 *            The level *
+	 * @return The new tree item
+	 */
+	private TreeItem newTreeItem(TreeItem parent, Object node, int level)
+	{
+		return new TreeItem(parent, "" + idCounter++, node, level);
+	}
+
+	/**
+	 * Creates a tree item for given node with specified id.
+	 * 
+	 * @param parent
+	 * @param node
+	 *            The tree node
+	 * @param level
+	 *            The level
+	 * @param id
+	 *            the component id
+	 * @return The new tree item
+	 */
+	private TreeItem newTreeItem(TreeItem parent, Object node, int level, String id)
+	{
+		return new TreeItem(parent, id, node, level);
+	}
+
+	/**
+	 * Return the representation of node children as Iterator interface.
+	 * 
+	 * @param node
+	 *            The tree node
+	 * @return iterable presentation of node children
+	 */
+	public final Iterator<Object> nodeChildren(Object node)
+	{
+		TreeModel model = getTreeModel();
+		int count = model.getChildCount(node);
+		List<Object> nodes = new ArrayList<Object>(count);
+		for (int i = 0; i < count; ++i)
+		{
+			nodes.add(model.getChild(node, i));
+		}
+		return nodes.iterator();
+	}
+
+	/**
+	 * @param parent
+	 * @param index
+	 * @return child
+	 */
+	public final Object getChildAt(Object parent, int index)
+	{
+		return getTreeModel().getChild(parent, index);
+	}
+
+	/**
+	 * 
+	 * @param node
+	 * @return boolean
+	 */
+	public final boolean isLeaf(Object node)
+	{
+		return getTreeModel().isLeaf(node);
+	}
+
+	/**
+	 * @param parent
+	 * @return child count
+	 */
+	public final int getChildCount(Object parent)
+	{
+		return getTreeModel().getChildCount(parent);
+	}
+
+	private TreeModel getTreeModel()
+	{
+		return getModelObject();
+	}
+
+	/**
+	 * Rebuilds children of every item in dirtyItems that needs it. This method is called for
+	 * non-partial update.
+	 */
+	private void rebuildDirty()
+	{
+		// go through dirty items
+		for (TreeItem item : dirtyItems)
+		{
+			// item children need to be rebuilt
+			if (item.getChildren() == null)
+			{
+				buildItemChildren(item);
+			}
+		}
+	}
+
+	/**
+	 * Removes the item, appends it's id to deleteIds. This is called when a items parent is being
+	 * deleted or rebuilt.
+	 * 
+	 * @param item
+	 *            The item to remove
+	 */
+	private void removeItem(TreeItem item)
+	{
+		// even if the item is dirty it's no longer necessary to update id
+		dirtyItems.remove(item);
+
+		// if the item was about to be created
+		if (dirtyItemsCreateDOM.contains(item))
+		{
+			// we needed to create DOM element, we no longer do
+			dirtyItemsCreateDOM.remove(item);
+		}
+		else
+		{
+			// add items id (it's short version) to ids of DOM elements that
+			// will be
+			// removed
+			deleteIds.append(getShortItemId(item));
+			deleteIds.append(",");
+		}
+
+		if (item.getParent() != null)
+		{
+			// remove the id
+			// note that this doesn't update item's parent's children list
+			item.remove();
+		}
+	}
+
+	/**
+	 * Calls after the tree has been rendered. Clears all dirty flags.
+	 */
+	private void updated()
+	{
+		dirtyAll = false;
+		dirtyItems.clear();
+		dirtyItemsCreateDOM.clear();
+		deleteIds.clear(); // FIXME: Recreate it to save some space?
+	}
+
+	/**
+	 * Call the callback#visitItem method for the given item and all it's children.
+	 * 
+	 * @param item
+	 *            The tree item
+	 * @param callback
+	 *            item call back
+	 */
+	private void visitItemAndChildren(TreeItem item, IItemCallback callback)
+	{
+		callback.visitItem(item);
+		visitItemChildren(item, callback);
+	}
+
+	/**
+	 * Call the callback#visitItem method for every child of given item.
+	 * 
+	 * @param item
+	 *            The tree item
+	 * @param callback
+	 *            The callback
+	 */
+	private void visitItemChildren(TreeItem item, IItemCallback callback)
+	{
+		if (item.getChildren() != null)
+		{
+			for (TreeItem child : item.getChildren())
+			{
+				visitItemAndChildren(child, callback);
+			}
+		}
+	}
+
+	/**
+	 * Returns the component associated with given node, or null, if node is not visible. This is
+	 * useful in situations when you want to touch the node element in html.
+	 * 
+	 * @param node
+	 *            Tree node
+	 * @return Component associated with given node, or null if node is not visible.
+	 */
+	public Component getNodeComponent(Object node)
+	{
+		return nodeToItemMap.get(node);
+	}
+
+	@Override
+	public void renderHead(IHeaderResponse response)
+	{
+		response.render(JavaScriptHeaderItem.forReference(JAVASCRIPT));
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/BaseTree.html
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/BaseTree.html b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/BaseTree.html
new file mode 100644
index 0000000..a123a26
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/BaseTree.html
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<wicket:panel xmlns:wicket="http://wicket.apache.org">
+<table wicket:id="i" class="wicket-tree-content"><tr>
+<a wicket:id="junctionLink"></a>
+<td wicket:id="nodeComponent"></td>
+</tr></table>
+</wicket:panel>
\ No newline at end of file