Merge branch 'master' of github.com:vaadin/gwt-site into jd_click_enhancement

Conflicts:
	src/main/java/com/google/gwt/site/webapp/client/GWTProjectEntryPoint.java
diff --git a/src/main/java/com/google/gwt/site/webapp/client/GWTProjectEntryPoint.java b/src/main/java/com/google/gwt/site/webapp/client/GWTProjectEntryPoint.java
index 06d2d14..f94ec72 100644
--- a/src/main/java/com/google/gwt/site/webapp/client/GWTProjectEntryPoint.java
+++ b/src/main/java/com/google/gwt/site/webapp/client/GWTProjectEntryPoint.java
@@ -19,25 +19,47 @@
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.query.client.Function;
 import com.google.gwt.query.client.GQuery;
+import com.google.gwt.regexp.shared.RegExp;
+import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.Window;
 
 import static com.google.gwt.query.client.GQuery.$;
+import static com.google.gwt.query.client.GQuery.body;
 
 public class GWTProjectEntryPoint implements EntryPoint {
-	private String currentPage;
+  private static final RegExp INTERNAL_URL_REGEX;
+  private static final RegExp NOT_RELATIVE_URL_REGEX;
+
+  static {
+    String domainUrl =  Window.Location.getHostName();
+    String port = Window.Location.getPort();
+
+    if (port != null && port.length() > 0) {
+      domainUrl += ":" + port;
+    }
+
+    INTERNAL_URL_REGEX = RegExp.compile("^(https?)\\:\\/\\/" + domainUrl + "\\/.*$");
+    NOT_RELATIVE_URL_REGEX = RegExp.compile("^(https?)|(mailto)|(ftp)|(javascript)\\:.*$");
+  }
+
+  private String currentPage;
 
 	@Override
 	public void onModuleLoad() {	
 		enhanceMenu();
     
-    openMenu();
+    openMenu(Window.Location.getPath());
+
+    if (supportsHtml5History()) {
+      initOnPopState();
+    }
 	}
 
-  private void openMenu() {
-    String href = Window.Location.getPath();
+  private void openMenu(String path) {
+    $("#gwt-toc a.selected").removeClass("selected");
 
-    if (href != null && href.length() > 0) {
-      $("#gwt-toc a[href$='" + href + "']").addClass("selected")
+    if (path != null && path.length() > 0) {
+      $("#gwt-toc a[href$='" + path + "']").addClass("selected")
           .parentsUntil("#gwt-toc").filter("li.folder").addClass("open")
           .children("ul").slideDown(200);
     }
@@ -53,18 +75,26 @@
 		});
 		
 		$("#gwt-toc li.folder > ul").css("display", "none");
- 
-		$("#gwt-toc a").click(new Function() {
-			@Override
-			public void f(Element e) {
-				loadPage(e.getAttribute("ahref"));
 
-        selectItem(e);
+    $("a", body).live(Event.ONCLICK, new Function() {
+      @Override
+      public boolean f(Event event) {
+        String href = getElement().getAttribute("href");
 
-				getEvent().preventDefault();
-			}
-		});
-	}
+        if (isInternalNavigation(href)) {
+          return loadPage(href, true);
+        }
+
+        return true;
+      }
+    });
+  }
+
+  private boolean isInternalNavigation(String href) {
+    // TODO mix the three last conditions in one regex !!
+    return href != null && href.length() > 0 && (!href.startsWith("#") && !NOT_RELATIVE_URL_REGEX.test(href)
+        || INTERNAL_URL_REGEX.test(href));
+  }
 
 	private void toggleMenu(GQuery menu) {
 		if (menu.hasClass("open")) {
@@ -76,34 +106,56 @@
 		menu.children("ul").slideToggle(200);
 	}
 
-	private void loadPage(String pageUrl) {
-		if (pageUrl.equals(currentPage)) {
-			return;
-		}
+  private boolean loadPage(final String pageUrl, final boolean pushState) {
+    int hashIndex = pageUrl.indexOf('#');
+    final String path = hashIndex != -1 ? pageUrl.substring(0, hashIndex) : pageUrl;
+    final String hash = hashIndex != -1 ? pageUrl.substring(hashIndex) : null;
 
-		if (supportsHtml5Histroy()) {
-			currentPage = pageUrl;
-			
+    if (supportsHtml5History() && !path.equals(currentPage)) {
+      currentPage = path;
 
-			$("#gwt-content").load(pageUrl + " #gwt-content > div");
+      $("#gwt-content").load(pageUrl + " #gwt-content > div", null, new Function(){
+          @Override
+          public void f() {
+            if (pushState) {
+              pushState(pageUrl);
+            }
+            if (hash != null) {
+              scrollTo(hash);
+            }
+            openMenu(path);
+          }
+        }
+      );
 
-			pushState(pageUrl);
-		} else {
-			Window.Location.replace(pageUrl);
-		}
+      return false;
+    } else if (hash != null) {
+      scrollTo(hash);
+    }
 
-	}
+    return !path.equals(currentPage);
+  }
 
-  private void selectItem(Element item) {
-    $("#gwt-toc a.selected").removeClass("selected");
-    item.addClassName("selected");
+  private void scrollTo(String hash) {
+    Window.scrollTo(Window.getScrollLeft(), $(hash).offset().top);
+  }
+
+  private void onPopState() {
+    loadPage(Window.Location.getPath(), false);
   }
 
 	private native boolean pushState(String url) /*-{
 		$wnd.history.pushState(null, null, url);
 	}-*/;
 
-	private native boolean supportsHtml5Histroy()/*-{
+	private native boolean supportsHtml5History()/*-{
 		return $wnd.history && $wnd.history.pushState;
 	}-*/;
+
+  private native void initOnPopState() /*-{
+      var that = this;
+      $wnd.onpopstate = $entry(function(e) {
+          that.@com.google.gwt.site.webapp.client.GWTProjectEntryPoint::onPopState()();
+      });
+  }-*/;
 }