Changes in menu to support latest menu changes.

- Updated gQuery version
- Use on/off instead of bind/live/click deprecated api.
- Use css3 animations through gquery.
- Expand/Collapse when clicking on the arrow like GWT tree does.
- Work in any webserver context, and in filesystem.
- Almost rewritten code so as we can simply regexp and
  remove usage of absolute attributes.

Bugs: #8235 #8236 #8629 #8707
Change-Id: I19df8de2d6d20d93796132a4fecc2ddec4dcb536
diff --git a/pom.xml b/pom.xml
index 3479d43..afc321a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,135 +1,141 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 
-	<modelVersion>4.0.0</modelVersion>
-	<groupId>com.google.gwt.site.webapp</groupId>
-	<artifactId>gwt-site-webapp</artifactId>
-	<version>1.0-SNAPSHOT</version>
-	<packaging>war</packaging>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.google.gwt.site.webapp</groupId>
+    <artifactId>gwt-site-webapp</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>war</packaging>
 
+    <properties>
+        <appengine.app.version>1</appengine.app.version>
+        <appengine.target.version>1.8.8</appengine.target.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <gwtversion>2.6.0</gwtversion>
+        <gwtquery.version>1.4.2</gwtquery.version>
+    </properties>
 
+    <dependencies>
+        <!-- Compile/runtime dependencies -->
+        <dependency>
+            <groupId>com.google.appengine</groupId>
+            <artifactId>appengine-api-1.0-sdk</artifactId>
+            <version>${appengine.target.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jstl</groupId>
+            <artifactId>jstl</artifactId>
+            <version>1.2</version>
+        </dependency>
 
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-server</artifactId>
+            <version>1.9</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-grizzly2</artifactId>
+            <version>1.9</version>
+        </dependency>
+        <dependency>
+            <groupId>org.json</groupId>
+            <artifactId>json</artifactId>
+            <version>20090211</version>
+        </dependency>
 
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>1.7</version>
+        </dependency>
 
+        <!-- Test Dependencies -->
+        <dependency>
+            <groupId>com.google.appengine</groupId>
+            <artifactId>appengine-testing</artifactId>
+            <version>${appengine.target.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.appengine</groupId>
+            <artifactId>appengine-api-stubs</artifactId>
+            <version>${appengine.target.version}</version>
+            <scope>test</scope>
+        </dependency>
 
-	<properties>
-		<appengine.app.version>1</appengine.app.version>
-		<appengine.target.version>1.8.8</appengine.target.version>
-		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-		<gwtversion>2.6.0</gwtversion>
-        <gwtquery.version>1.3.2</gwtquery.version>
-	</properties>
+        <dependency>
+            <groupId>com.sun.jersey.contribs</groupId>
+            <artifactId>jersey-guice</artifactId>
+            <version>1.7</version>
+        </dependency>
 
-	<dependencies>
-		<!-- Compile/runtime dependencies -->
-		<dependency>
-			<groupId>com.google.appengine</groupId>
-			<artifactId>appengine-api-1.0-sdk</artifactId>
-			<version>${appengine.target.version}</version>
-		</dependency>
-		<dependency>
-			<groupId>javax.servlet</groupId>
-			<artifactId>servlet-api</artifactId>
-			<version>2.5</version>
-			<scope>provided</scope>
-		</dependency>
-		<dependency>
-			<groupId>jstl</groupId>
-			<artifactId>jstl</artifactId>
-			<version>1.2</version>
-		</dependency>
+        <dependency>
+            <groupId>javax.jdo</groupId>
+            <artifactId>jdo-api</artifactId>
+            <version>3.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.datanucleus</groupId>
+            <artifactId>datanucleus-core</artifactId>
+            <version>3.1.1</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.datanucleus</groupId>
+            <artifactId>datanucleus-api-jdo</artifactId>
+            <version>3.1.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.appengine.orm</groupId>
+            <artifactId>datanucleus-appengine</artifactId>
+            <version>2.1.2</version>
+        </dependency>
 
-		<dependency>
-			<groupId>com.sun.jersey</groupId>
-			<artifactId>jersey-server</artifactId>
-			<version>1.9</version>
-		</dependency>
-		<dependency>
-			<groupId>com.sun.jersey</groupId>
-			<artifactId>jersey-grizzly2</artifactId>
-			<version>1.9</version>
-		</dependency>
-		<dependency>
-			<groupId>org.json</groupId>
-			<artifactId>json</artifactId>
-			<version>20090211</version>
-		</dependency>
+        <dependency>
+            <groupId>com.google.gwt</groupId>
+            <artifactId>gwt-user</artifactId>
+            <version>${gwtversion}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+            <version>1.0.0.GA</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+            <version>1.0.0.GA</version>
+            <classifier>sources</classifier>
+            <scope>provided</scope>
+        </dependency>
 
-		<dependency>
-			<groupId>commons-codec</groupId>
-			<artifactId>commons-codec</artifactId>
-			<version>1.7</version>
-		</dependency>
-
-		<!-- Test Dependencies -->
-		<dependency>
-			<groupId>com.google.appengine</groupId>
-			<artifactId>appengine-testing</artifactId>
-			<version>${appengine.target.version}</version>
-			<scope>test</scope>
-		</dependency>
-		<dependency>
-			<groupId>com.google.appengine</groupId>
-			<artifactId>appengine-api-stubs</artifactId>
-			<version>${appengine.target.version}</version>
-			<scope>test</scope>
-		</dependency>
-
-		<dependency>
-			<groupId>com.sun.jersey.contribs</groupId>
-			<artifactId>jersey-guice</artifactId>
-			<version>1.7</version>
-		</dependency>
-
-		<dependency>
-			<groupId>javax.jdo</groupId>
-			<artifactId>jdo-api</artifactId>
-			<version>3.0.1</version>
-		</dependency>
-		<dependency>
-			<groupId>org.datanucleus</groupId>
-			<artifactId>datanucleus-core</artifactId>
-			<version>3.1.1</version>
-			<scope>runtime</scope>
-		</dependency>
-		<dependency>
-			<groupId>org.datanucleus</groupId>
-			<artifactId>datanucleus-api-jdo</artifactId>
-			<version>3.1.2</version>
-		</dependency>
-		<dependency>
-			<groupId>com.google.appengine.orm</groupId>
-			<artifactId>datanucleus-appengine</artifactId>
-			<version>2.1.2</version>
-		</dependency>
-
-		<dependency>
-			<groupId>com.google.gwt</groupId>
-			<artifactId>gwt-user</artifactId>
-			<version>${gwtversion}</version>
-			<scope>provided</scope>
-		</dependency>
-		<dependency>
-			<groupId>javax.validation</groupId>
-			<artifactId>validation-api</artifactId>
-			<version>1.0.0.GA</version>
-			<scope>provided</scope>
-		</dependency>
-		<dependency>
-			<groupId>javax.validation</groupId>
-			<artifactId>validation-api</artifactId>
-			<version>1.0.0.GA</version>
-			<classifier>sources</classifier>
-			<scope>provided</scope>
-		</dependency>
-
-		<dependency>
-			<groupId>javax.persistence</groupId>
-			<artifactId>persistence-api</artifactId>
-			<version>1.0</version>
-		</dependency>
-
+        <dependency>
+            <groupId>javax.persistence</groupId>
+            <artifactId>persistence-api</artifactId>
+            <version>1.0</version>
+        </dependency>
+        <dependency>
+                <groupId>junit</groupId>
+                <artifactId>junit</artifactId>
+                <version>3.8.1</version>
+                <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.gwt</groupId>
+            <artifactId>gwt-dev</artifactId>
+            <version>${gwtversion}</version>
+            <scope>test</scope>
+        </dependency>
         <!-- UI dependencies -->
         <dependency>
             <groupId>com.googlecode.gwtquery</groupId>
@@ -137,148 +143,168 @@
             <version>${gwtquery.version}</version>
             <scope>provided</scope>
         </dependency>
-	</dependencies>
+    </dependencies>
 
-	<build>
-		<plugins>
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<version>2.5.1</version>
-				<artifactId>maven-compiler-plugin</artifactId>
-				<configuration>
-					<source>1.6</source>
-					<target>1.6</target>
-				</configuration>
-			</plugin>
+    <build>
+        <testResources>
+            <testResource>
+                <directory>${basedir}/src/test/java</directory>
+            </testResource>
+            <testResource>
+                <directory>${basedir}/src/test/resources</directory>
+            </testResource>
+        </testResources>
+        <plugins>
+            <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-surefire-plugin</artifactId>
+              <version>2.6</version>
+              <configuration>
+                 <additionalClasspathElements>
+                   <additionalClasspathElement>${basedir}/src/main/java</additionalClasspathElement>
+                   <additionalClasspathElement>${basedir}/src/test/java</additionalClasspathElement>
+                 </additionalClasspathElements>
+                 <useSystemClassLoader>false</useSystemClassLoader>
+              </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <version>2.5.1</version>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
 
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-war-plugin</artifactId>
-				<version>2.3</version>
-				<configuration>
-					<!-- <archiveClasses>true</archiveClasses> -->
-					<webResources>
-						<!-- in order to interpolate version from pom into appengine-web.xml -->
-						<resource>
-							<directory>${basedir}/src/main/webapp/WEB-INF</directory>
-							<filtering>true</filtering>
-							<targetPath>WEB-INF</targetPath>
-						</resource>
-						<!-- copy GWT source code to website for debugging -->
-						<resource>
-							<directory>${basedir}/target/extra/gwtproject/src</directory>
-							<targetPath>src</targetPath>
-						</resource>
-						<!-- copy sourcemaps to website for debugging -->
-						<resource>
-							<directory>${basedir}/target/extra/gwtproject/symbolMaps</directory>
-							<includes>
-                				<include>*.json</include>
-        					</includes>
-							<targetPath>src</targetPath>
-						</resource>
-					</webResources>
-				</configuration>
-			</plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <version>2.3</version>
+                <configuration>
+                    <!-- <archiveClasses>true</archiveClasses> -->
+                    <webResources>
+                        <!-- in order to interpolate version from pom into appengine-web.xml -->
+                        <resource>
+                            <directory>${basedir}/src/main/webapp/WEB-INF</directory>
+                            <filtering>true</filtering>
+                            <targetPath>WEB-INF</targetPath>
+                        </resource>
+                        <!-- copy GWT source code to website for debugging -->
+                        <resource>
+                            <directory>${basedir}/target/extra/gwtproject/src</directory>
+                            <targetPath>src</targetPath>
+                        </resource>
+                        <!-- copy sourcemaps to website for debugging -->
+                        <resource>
+                            <directory>${basedir}/target/extra/gwtproject/symbolMaps</directory>
+                            <includes>
+                                <include>*.json</include>
+                            </includes>
+                            <targetPath>src</targetPath>
+                        </resource>
+                    </webResources>
+                </configuration>
+            </plugin>
 
-			<plugin>
-				<groupId>com.google.appengine</groupId>
-				<artifactId>appengine-maven-plugin</artifactId>
-				<version>${appengine.target.version}</version>
-				<configuration>
-				  <address>0.0.0.0</address>
-				</configuration>
-			</plugin>
+            <plugin>
+                <groupId>com.google.appengine</groupId>
+                <artifactId>appengine-maven-plugin</artifactId>
+                <version>${appengine.target.version}</version>
+                <configuration>
+                  <address>0.0.0.0</address>
+                </configuration>
+            </plugin>
 
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-eclipse-plugin</artifactId>
-				<version>2.8</version>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-eclipse-plugin</artifactId>
+                <version>2.8</version>
 
-				<configuration>
-					<downloadSources>true</downloadSources>
-					<downloadJavadocs>false</downloadJavadocs>
-					<buildOutputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes</buildOutputDirectory>
-					<projectnatures>
-						<projectnature>org.eclipse.jdt.core.javanature</projectnature>
-						<projectnature>com.google.gdt.eclipse.core.webAppNature</projectnature>
-						<nature>com.google.appengine.eclipse.core.gaeNature</nature>
-						<nature>com.google.gwt.eclipse.core.gwtNature</nature>
-					</projectnatures>
-					<buildcommands>
-						<buildcommand>org.eclipse.jdt.core.javabuilder</buildcommand>
-						<buildcommand>com.google.gdt.eclipse.core.webAppProjectValidator</buildcommand>
+                <configuration>
+                    <downloadSources>true</downloadSources>
+                    <downloadJavadocs>false</downloadJavadocs>
+                    <buildOutputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes</buildOutputDirectory>
+                    <projectnatures>
+                        <projectnature>org.eclipse.jdt.core.javanature</projectnature>
+                        <projectnature>com.google.gdt.eclipse.core.webAppNature</projectnature>
+                        <nature>com.google.appengine.eclipse.core.gaeNature</nature>
+                        <nature>com.google.gwt.eclipse.core.gwtNature</nature>
+                    </projectnatures>
+                    <buildcommands>
+                        <buildcommand>org.eclipse.jdt.core.javabuilder</buildcommand>
+                        <buildcommand>com.google.gdt.eclipse.core.webAppProjectValidator</buildcommand>
 
-						<buildcommand>com.google.appengine.eclipse.core.projectValidator</buildcommand>
-						<buildcommand>com.google.gwt.eclipse.core.gwtProjectValidator</buildcommand>
-						<buildcommand>com.google.appengine.eclipse.core.enhancerbuilder</buildcommand>
+                        <buildcommand>com.google.appengine.eclipse.core.projectValidator</buildcommand>
+                        <buildcommand>com.google.gwt.eclipse.core.gwtProjectValidator</buildcommand>
+                        <buildcommand>com.google.appengine.eclipse.core.enhancerbuilder</buildcommand>
 
-					</buildcommands>
-					<classpathContainers>
-						<classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
-						<classpathContainer>com.google.appengine.eclipse.core.GAE_CONTAINER</classpathContainer>
-						<classpathContainer>com.google.gwt.eclipse.core.GWT_CONTAINER</classpathContainer>
-					</classpathContainers>
-					<excludes>
-						<exclude>com.google.gwt:gwt-servlet</exclude>
-						<exclude>com.google.gwt:gwt-user</exclude>
-						<exclude>com.google.gwt:gwt-dev</exclude>
-						<exclude>javax.validation:validation-api</exclude>
-						<exclude>com.google.appengine:appengine-api-1.0-sdk</exclude>
-						<exclude>com.google.appengine:appengine-api-stubs</exclude>
-						<exclude>com.google.appengine:appengine-testing</exclude>
-						<exclude>org.datanucleus:datanucleus-core</exclude>
-						<exclude>org.datanucleus:datanucleus-enhancer</exclude>
-						<exclude>org.datanucleus:datanucleus-jpa</exclude>
-						<exclude>org.datanucleus:datanucleus-api-jdo</exclude>
-						<exclude>com.google.appengine.orm:datanucleus-appengine</exclude>
-					</excludes>
+                    </buildcommands>
+                    <classpathContainers>
+                        <classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
+                        <classpathContainer>com.google.appengine.eclipse.core.GAE_CONTAINER</classpathContainer>
+                        <classpathContainer>com.google.gwt.eclipse.core.GWT_CONTAINER</classpathContainer>
+                    </classpathContainers>
+                    <excludes>
+                        <exclude>com.google.gwt:gwt-servlet</exclude>
+                        <exclude>com.google.gwt:gwt-user</exclude>
+                        <exclude>com.google.gwt:gwt-dev</exclude>
+                        <exclude>javax.validation:validation-api</exclude>
+                        <exclude>com.google.appengine:appengine-api-1.0-sdk</exclude>
+                        <exclude>com.google.appengine:appengine-api-stubs</exclude>
+                        <exclude>com.google.appengine:appengine-testing</exclude>
+                        <exclude>org.datanucleus:datanucleus-core</exclude>
+                        <exclude>org.datanucleus:datanucleus-enhancer</exclude>
+                        <exclude>org.datanucleus:datanucleus-jpa</exclude>
+                        <exclude>org.datanucleus:datanucleus-api-jdo</exclude>
+                        <exclude>com.google.appengine.orm:datanucleus-appengine</exclude>
+                    </excludes>
 
 
-				</configuration>
-			</plugin>
+                </configuration>
+            </plugin>
 
-			<plugin>
-				<groupId>org.datanucleus</groupId>
-				<artifactId>maven-datanucleus-plugin</artifactId>
-				<version>3.1.2</version>
-				<configuration>
-					<log4jConfiguration>${basedir}/log4j.properties</log4jConfiguration>
-					<verbose>false</verbose>
-					<fork>false</fork>
-				</configuration>
-				<executions>
-					<execution>
-						<phase>process-classes</phase>
-						<goals>
-							<goal>enhance</goal>
-						</goals>
-					</execution>
-				</executions>
-			</plugin>
+            <plugin>
+                <groupId>org.datanucleus</groupId>
+                <artifactId>maven-datanucleus-plugin</artifactId>
+                <version>3.1.2</version>
+                <configuration>
+                    <log4jConfiguration>${basedir}/log4j.properties</log4jConfiguration>
+                    <verbose>false</verbose>
+                    <fork>false</fork>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>enhance</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
 
-			<plugin>
-				<groupId>org.codehaus.mojo</groupId>
-				<artifactId>gwt-maven-plugin</artifactId>
-				<version>${gwtversion}</version>
-				<executions>
-					<execution>
-						<configuration>
-							<module>com.google.gwt.site.webapp.GWTProject</module>
-							<runTarget>index.html</runTarget>
-							<copyWebapp>true</copyWebapp>
-							<saveSource>true</saveSource>
-							<draftCompile>true</draftCompile>
-							<style>PRETTY</style>
-						</configuration>
-						<goals>
-							<goal>compile</goal>
-							<goal>test</goal>
-						</goals>
-					</execution>
-				</executions>
-			</plugin>
-		</plugins>
-	</build>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>gwt-maven-plugin</artifactId>
+                <version>${gwtversion}</version>
+                <executions>
+                    <execution>
+                        <configuration>
+                            <module>com.google.gwt.site.webapp.GWTProject</module>
+                            <runTarget>index.html</runTarget>
+                            <copyWebapp>true</copyWebapp>
+                            <saveSource>true</saveSource>
+                            <draftCompile>true</draftCompile>
+                            <style>PRETTY</style>
+                        </configuration>
+                        <goals>
+                            <goal>compile</goal>
+                            <goal>test</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
 
 </project>
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 8000507..fff78ff 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
@@ -15,8 +15,13 @@
  */
 package com.google.gwt.site.webapp.client;
 
+import static com.google.gwt.query.client.GQuery.$;
+import static com.google.gwt.query.client.GQuery.body;
+import static com.google.gwt.query.client.GQuery.window;
+
 import com.google.gwt.core.client.EntryPoint;
 import com.google.gwt.core.client.GWT;
+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.query.client.Properties;
@@ -26,58 +31,67 @@
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.ui.impl.HyperlinkImpl;
 
-import static com.google.gwt.query.client.GQuery.$;
-import static com.google.gwt.query.client.GQuery.body;
-import static com.google.gwt.query.client.GQuery.window;
-
 public class GWTProjectEntryPoint implements EntryPoint {
 
-  private static final RegExp isSameOriginRexp;
+  private static final int ANIMATION_TIME = 200;
+
   private static final HyperlinkImpl clickHelper = GWT.create(HyperlinkImpl.class);
+
   private static Properties history = JsUtils.prop(window, "history");
+
+  // Visible for testing
+  // The absolute path to the url root (http://gwtproject.com)
+  static String origin = GWT.getModuleBaseForStaticFiles()
+      .replaceFirst("^(\\w+://.+?)/.*", "$1").toLowerCase();
+
+  // Visible for testing
+  // We discard links with different origin, hashes, starting with protocol, javadocs, and media links.
+  static final RegExp isSameOriginRexp = RegExp.compile("^" + origin
+      + "|^(?!(#|[a-z#]+:))(?!.*(|/)javadoc/)(?!.*\\.(jpe?g|png|mpe?g|mp[34]|avi)$)", "i");
+
   private static boolean isPushstateCapable = history.get("pushState") != null;
-
-  static {
-    // XHR must match all: protocol, host and port.
-    // Note: in chrome this could be simpler since it has window.location.origin
-    String origin = Window.Location.getProtocol() + "//" + Window.Location.getHostName();
-    String port = Window.Location.getPort();
-    if (port != null && port.length() > 0) {
-      origin += ":" + port;
-    }
-    // We discard links with a different origin, hash links and protocol-agnostic urls.
-    isSameOriginRexp = RegExp.compile("^" + origin + "|^(?!(https?|mailto|ftp|javascript):|#|//).+", "i");
-  }
-
-  private String currentPage;
+  private static boolean ajaxEnabled = isPushstateCapable && origin.startsWith("http");
+  private static String currentPage = Window.Location.getPath();
 
   @Override
   public void onModuleLoad() {
-    enhanceMenu();
-
+    enhancePage();
+    $("#gwt-toc li ul").hide();
     openMenu();
-
-    maybeBindPopState();
-
-    currentPage = Window.Location.getPath();
   }
 
+  /*
+   * Open the branch and select the item corresponding to the current url.
+   */
   private void openMenu() {
+    GQuery item = $("#gwt-toc a[href='" + Window.Location.getPath() + "']").eq(0);
+
+    // Only collapse unrelated entries in mobile
+    if ($("#nav-mobile").isVisible()) {
+       hideUnrelatedBranches(item);
+    }
+
+    showBranch(item);
+
     $("#gwt-toc a.selected").removeClass("selected");
+    item.addClass("selected");
 
-    String path = Window.Location.getPath();
-
-    $("#gwt-toc a[ahref$='" + path + "']")
-        .addClass("selected")
-        .parentsUntil("#gwt-toc")
-        .filter("li.folder")
-        .addClass("open")
-        .children("ul")
-        .slideDown(200);
+    // Change the page title for easy bookmarking
+    $("title").text("[GWT] " + item.text());
   }
 
-  private void enhanceMenu() {
-    $("li.folder > a").click(new Function() {
+  /*
+   * Enhance the page adding handlers and replacing relative by absolute urls
+   */
+  private void enhancePage() {
+
+    // We add a span with the +/- icon so as the click area is well defined
+    // this span is not rendered in server side because it is only needed
+    // for enhancing the page.
+    GQuery parentItems = $("#gwt-toc li").has("ul").prepend("<span/>");
+
+    // Toggle the branch when clicking on the arrow or anchor without content
+    $(parentItems).children("span, a[href='#']").on("click", new Function() {
       @Override
       public boolean f(Event e) {
         toggleMenu($(e).parent());
@@ -85,104 +99,124 @@
       }
     });
 
-    $("#gwt-toc li.folder > ul").hide();
+    // Replace relative paths in anchors by absolute ones
+    // exclude all anchors in the content area.
+    $("a").not($("#gwt-content a")).each(new Function() {
+      @Override
+      public void f(Element e) {
+        GQuery link = $(e);
+        if (shouldEnhanceLink(link)) {
+          // No need to make complicated things for computing
+          // the absolute path: anchor.pathname is the way
+          link.attr("href", link.prop("pathname"));
+        }
+      }
+    });
 
-    $("a", body).live(Event.ONCLICK, new Function() {
+    // Do not continue enhancing if Ajax is disabled
+    if (!ajaxEnabled) {
+      // Select current item from the URL info
+      loadPage(null);
+      return;
+    }
+
+    // Use Ajax instead of default behaviour
+    $(body).on("click", "a", new Function() {
       @Override
       public boolean f(Event e) {
-        String href = getHref($(e));
-        if (shouldEnhanceClick(e) && isPushstateCapable && isSameOriginRexp.test(href)) {
-          loadPage(href);
+        if (shouldEnhanceLink($(e)) &&
+            // Is it a normal click (not ctrl/cmd/shift/right/middle click) ?
+            clickHelper.handleAsClick(e)) {
+
+          // Load the page using Ajax
+          loadPage($(e));
           return false;
         }
         return true;
       }
     });
-  }
 
-  private boolean shouldEnhanceClick(Event e) {
-    GQuery link = $(e);
-
-    // Is it a normal click (not ctrl/cmd/shift/right/middle click) ?
-    if (!clickHelper.handleAsClick(e)) {
-      return false;
-    }
-
-    // do not load links that are marked as full page reload
-    if ("true".equals(link.attr("data-full-load"))) {
-      return false;
-    }
-
-    // do not load javadoc async
-    if (getHref(link).startsWith("/javadoc/")) {
-      return false;
-    }
-
-    return true;
-  }
-
-  /**
-   * Sometimes the link src comes in the 'ahref' attribute
-   */
-  private String getHref(GQuery link) {
-    return  JsUtils.or(link.attr("ahref"), link.attr("href"));
-  }
-
-  private void toggleMenu(GQuery menu) {
-    menu.toggleClass("open")
-        .children("ul")
-        .slideToggle(200);
-  }
-
-  private void loadPage(String pageUrl) {
-    if (pageUrl != null) {
-      JsUtils.runJavascriptFunction(history, "pushState", null, null, pageUrl);
-    }
-
-    String path = Window.Location.getPath();
-    final String hash = Window.Location.getHash();
-
-    if (!path.equals(currentPage)) {
-      currentPage = path;
-
-      $("#gwt-content").load(path + " #gwt-content > div", null,
-          new Function() {
-            @Override
-            public void f() {
-              scrollTo(hash);
-              openMenu();
-            }
-          });
-
-    } else {
-      scrollTo(hash);
-    }
-  }
-
-  private void scrollTo(String hash) {
-    if (hash == null || hash.length() == 0) {
-      Window.scrollTo(0, 0);
-    } else {
-      GQuery anchor = $(hash);
-      if (anchor.isEmpty()) {
-        anchor = $("[name='" + hash.substring(1) + "']");
-      }
-
-      anchor.scrollIntoView();
-    }
-  }
-
-  private void maybeBindPopState() {
-    if (!isPushstateCapable) {
-      return;
-    }
-
-    // Note: gQuery will support $(window).on("popstate", function) in future releases.
-    window.<Properties>cast().setFunction("onpopstate", new Function() {
+    // Select the TOC item when URL changes
+    $(window).on("popstate", new Function() {
       @Override
       public void f() {
         loadPage(null);
       }
     });
   }
+
+  private boolean shouldEnhanceLink(GQuery link) {
+    return
+      // Enhance only local links
+      isSameOriginRexp.test(link.attr("href")) &&
+      // Do not load links that are marked as full page reload
+      !Boolean.parseBoolean(link.attr("data-full-load"));
+  }
+
+  private void toggleMenu(GQuery menu) {
+    menu.toggleClass("open")
+      .children("ul")
+      .slideToggle(ANIMATION_TIME);
+  }
+
+  private void hideUnrelatedBranches(GQuery item) {
+    $("#gwt-toc li.open")
+      .not(item).not(item.parents())
+      .removeClass("open")
+      .children("ul")
+      .slideUp(ANIMATION_TIME);
+  }
+
+  private void showBranch(GQuery item) {
+    item.parents()
+      .filter("li")
+      .addClass("open")
+      .children("ul")
+      .slideDown(ajaxEnabled ? ANIMATION_TIME : 0);
+  }
+
+  /*
+   * Change URL via pushState and load the page via Ajax.
+   */
+  private void loadPage(GQuery link) {
+    String pageUrl = link == null ? null : link.<String>prop("pathname");
+
+    if (!currentPage.equals(pageUrl)) {
+      if (pageUrl != null) {
+        // Preserve QueryString, useful for the gwt.codesvr parameter in dev-mode.
+        pageUrl = pageUrl.replaceFirst("(#.*|)$", Window.Location.getQueryString() + "$1");
+        // Set the page to load in the URL
+        JsUtils.runJavascriptFunction(history, "pushState", null, null, pageUrl);
+      }
+
+      pageUrl = Window.Location.getPath();
+      if (!currentPage.equals(pageUrl)) {
+        $("#spinner").show();
+        $("#gwt-content").load(pageUrl + " #gwt-content > div", null, new Function() {
+          @Override
+          public void f() {
+            openMenu();
+            scrollToHash();
+            $("#spinner").hide();
+          }
+        });
+      } else {
+        scrollToHash();
+      }
+      currentPage = pageUrl;
+    }
+  }
+
+  /*
+   * Move the scroll to the hash fragment in the URL
+   */
+  private void scrollToHash() {
+    String hash = Window.Location.getHash();
+    GQuery anchor = hash.length() > 1 ? $(hash + ", [name='" + hash.substring(1) + "']") : $();
+    if (anchor.isEmpty()) {
+      Window.scrollTo(0, 0);
+    } else {
+      anchor.scrollIntoView();
+    }
+  }
 }
diff --git a/src/test/java/com/google/gwt/site/webapp/Test.gwt.xml b/src/test/java/com/google/gwt/site/webapp/Test.gwt.xml
new file mode 100644
index 0000000..8a6351f
--- /dev/null
+++ b/src/test/java/com/google/gwt/site/webapp/Test.gwt.xml
@@ -0,0 +1,4 @@
+<module rename-to="gwtproject">
+  <inherits name="com.google.gwt.user.User" />
+  <inherits name="com.google.gwt.query.Query"/>
+</module>
diff --git a/src/test/java/com/google/gwt/site/webapp/client/GWTProjectEntryPointTest.java b/src/test/java/com/google/gwt/site/webapp/client/GWTProjectEntryPointTest.java
new file mode 100644
index 0000000..5f04e46
--- /dev/null
+++ b/src/test/java/com/google/gwt/site/webapp/client/GWTProjectEntryPointTest.java
@@ -0,0 +1,21 @@
+package com.google.gwt.site.webapp.client;
+
+import com.google.gwt.regexp.shared.RegExp;
+
+/**
+ * Test class for the GWTProject entry-point for running in GWT
+ */
+public class GWTProjectEntryPointTest extends JREProjectEntryPointTest {
+
+  public String getModuleName() {
+    return "com.google.gwt.site.webapp.Test";
+  }
+
+  protected String getOrigin() {
+    return GWTProjectEntryPoint.origin;
+  }
+
+  protected RegExp getSameOriginRegex() {
+    return GWTProjectEntryPoint.isSameOriginRexp;
+  }
+}
diff --git a/src/test/java/com/google/gwt/site/webapp/client/JREProjectEntryPointTest.java b/src/test/java/com/google/gwt/site/webapp/client/JREProjectEntryPointTest.java
new file mode 100644
index 0000000..60cdb63
--- /dev/null
+++ b/src/test/java/com/google/gwt/site/webapp/client/JREProjectEntryPointTest.java
@@ -0,0 +1,61 @@
+package com.google.gwt.site.webapp.client;
+
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.regexp.shared.RegExp;
+/**
+ * Test class for the GWTProject entry-point running in JVM
+ */
+public class JREProjectEntryPointTest extends GWTTestCase {
+
+  public String getModuleName() {
+    return null;
+  }
+
+  /*
+   * We need this method because GWTProjectEntryPoint.absoluteUrl cannot
+   * be instantiated in the JVM
+   */
+  protected String getOrigin() {
+    String absoluteUrl = "HTTP://AAA.COM/adfaf/adsfa.html?aa=b&c=d#aaa:aaa";
+
+    // This is the same Regexp that we use in client class
+    String origin = absoluteUrl.replaceFirst("^(\\w+://.+?)/.*", "$1").toLowerCase();
+    assertEquals("http://aaa.com", origin);
+
+    return origin;
+  }
+
+  /*
+   * We need this method because GWTProjectEntryPoint.isSameOriginRexp cannot
+   * be instantiated in the JVM
+   */
+  protected RegExp getSameOriginRegex() {
+    String origin = getOrigin();
+    // This is the same Regexp that we use in client class
+    return RegExp.compile("^" + origin + "|^(?!(#|[a-z#]+:))(?!.*(|/)javadoc/)(?!.*\\.(jpe?g|png|mpe?g|mp[34]|avi)$)", "i");
+  }
+
+  public void testEntryPointRegex() {
+    RegExp isSameOriginRexp = getSameOriginRegex();
+
+    assertTrue(isSameOriginRexp.test(getOrigin() + "/adfaf"));
+    assertFalse(isSameOriginRexp.test(getOrigin().replace("http", "https") + "/adfaf"));
+    assertFalse(isSameOriginRexp.test("file:///aaa.com/adfaf"));
+    assertFalse(isSameOriginRexp.test("telnet:///aaa.com/adfaf"));
+    assertFalse(isSameOriginRexp.test("ftp:///aaa.com/adfaf"));
+    assertFalse(isSameOriginRexp.test("mailto:foo@bar"));
+    assertTrue(isSameOriginRexp.test(".aaa"));
+    assertTrue(isSameOriginRexp.test("./aaa"));
+    assertTrue(isSameOriginRexp.test("/aaa"));
+    assertTrue(isSameOriginRexp.test("///aaa"));
+    assertFalse(isSameOriginRexp.test("#adfaf"));
+    assertFalse(isSameOriginRexp.test("#"));
+    assertTrue(isSameOriginRexp.test(""));
+    assertFalse(isSameOriginRexp.test("/javadoc/foo/bar"));
+    assertFalse(isSameOriginRexp.test("javadoc/foo/bar"));
+    assertFalse(isSameOriginRexp.test("whatever/javadoc/foo/bar"));
+    assertFalse(isSameOriginRexp.test("whatever/foo/bar.jpg"));
+    assertFalse(isSameOriginRexp.test("whatever/foo/bar.mp4"));
+    assertTrue(isSameOriginRexp.test("whatever/foo/bar.mp4foo"));
+  }
+}