Adds to HTMLPanel:
- addAndReplaceElement()
- getElementById()

Patch by: jgw
Review by: scottb, knorton
Issues: 1616, 2357

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2701 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/HTMLPanel.java b/user/src/com/google/gwt/user/client/ui/HTMLPanel.java
index 3e6a458..b2e9d03 100644
--- a/user/src/com/google/gwt/user/client/ui/HTMLPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/HTMLPanel.java
@@ -26,6 +26,8 @@
  */
 public class HTMLPanel extends ComplexPanel {
 
+  private static Element hiddenDiv;
+
   /**
    * A helper method for creating unique IDs for elements within dynamically-
    * generated HTML. This is important because no two elements in a document
@@ -37,8 +39,6 @@
     return DOM.createUniqueId();
   }
 
-  private Element hiddenDiv;
-
   /**
    * Creates an HTML panel with the specified HTML contents. Any element within
    * this HTML that has a specified id can contain a child widget.
@@ -58,8 +58,7 @@
    * @param id the id of the element within which it will be contained
    */
   public void add(Widget widget, String id) {
-    final Element elem = (isAttached()) ? DOM.getElementById(id)
-        : attachToDomAndGetElement(id);
+    final Element elem = getElementById(id);
 
     if (elem == null) {
       throw new NoSuchElementException(id);
@@ -68,15 +67,35 @@
     super.add(widget, elem);
   }
 
-  @Override
-  protected void onLoad() {
-    if (hiddenDiv != null) {
-      // This widget's element has already been moved, just need to remove the
-      // hidden DIV.
-      DOM.removeChild(RootPanel.getBodyElement(), hiddenDiv);
-      hiddenDiv = null;
+  /**
+   * Adds a child widget to the panel, replacing the HTML element specified by a
+   * given id.
+   * 
+   * @param widget the widget to be added
+   * @param id the id of the element to be replaced by the widget
+   */
+  public void addAndReplaceElement(Widget widget, String id) {
+    final Element toReplace = getElementById(id);
+
+    if (toReplace == null) {
+      throw new NoSuchElementException(id);
     }
-    super.onLoad();
+
+    // Logic pulled from super.add(), replacing the element rather than adding.
+    widget.removeFromParent();
+    getChildren().add(widget);
+    toReplace.getParentNode().replaceChild(widget.getElement(), toReplace);
+    adopt(widget);
+  }
+
+  /**
+   * Finds an {@link Element element} within this panel by its id. 
+   * 
+   * @param id the id of the element to be found
+   * @return the element with the given id, or <code>null</code> if none is found
+   */
+  public Element getElementById(String id) {
+    return isAttached() ? DOM.getElementById(id) : attachToDomAndGetElement(id);
   }
 
   /**
diff --git a/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java b/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java
index 72494ec..8d6986d 100644
--- a/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/HTMLPanelTest.java
@@ -15,8 +15,10 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.Node;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
 
 /**
  * Tests the HTMLPanel widget.
@@ -98,4 +100,34 @@
     assertTrue(DOM.getNextSibling(ba.getElement()) == hp.getElement());
     assertTrue(DOM.getNextSibling(hp.getElement()) == bb.getElement());
   }
+
+  /**
+   * Ensures that addAndReplaceChild() puts the widget in exactly the right place in the DOM.
+   */
+  public void testAddAndReplaceElement() {
+    HTMLPanel hp = new HTMLPanel("<div id='parent'>foo<span id='placeholder'></span>bar</div>");
+
+    Button button = new Button("my button");
+    hp.addAndReplaceElement(button, "placeholder");
+
+    assertEquals("parent", button.getElement().getParentElement().getId());
+
+    Node prev = button.getElement().getPreviousSibling();
+    assertEquals("foo", prev.getNodeValue());
+
+    Node next = button.getElement().getNextSibling();
+    assertEquals("bar", next.getNodeValue());
+  }
+
+  public void testGetElementById() {
+    HTMLPanel hp = new HTMLPanel("foo<div id='toFind'>bar</div>baz");
+
+    // Get the element twice, once before and once after attachment.
+    Element elem0 = hp.getElementById("toFind");
+    RootPanel.get().add(hp);
+    Element elem1 = hp.getElementById("toFind");
+
+    // Make sure we got the same element in both cases.
+    assertEquals(elem0, elem1);
+  }
 }