Markdown: configure optional banner in navbar.md

The navbar.md file can now define a documentation set header and
optional project logo:

  # Gerrit Code Review
  [logo]: http://storage.googleapis.com/gerrit-static/diffy-w200.png
  [home]: /index.md

  * [Home][home]
  * [APIs](/api/index.md)
  * [Source](/src/main/java/index.md)

The optional site header must be an H1 header and must be specified to
use the logo or home references.

The optional project logo is specified by a reference named "logo".
As images (or any other file type) are not served directly from
Gitiles this must be a link to an external service.  Recommended size
is no taller than 44px.

The optional "home" reference will create an <a href> to wrap the logo
and H1 inside an anchor to jump back to the logical top of the
documentation set. It can also be used inside the outline part of the
navbar to create a "Home" link.

These nodes are extracted from navbar.md and hoisted into the top of
the Soy template above the navbar's usual rendering.

   +-------+
   | Diffy |                       |
   |       |   Gerrit Code Review  |  APIs
   | Logo  |                       |
   +-------+

   Home    APIs    Source

The current page's H1 title is extracted and displayed to the right,
e.g. "APIs" above.

Change-Id: I70b4fd5dd51d4a92d328692740dd6cfa9655e84c
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/doc/Navbar.java b/gitiles-servlet/src/main/java/com/google/gitiles/doc/Navbar.java
new file mode 100644
index 0000000..784f990
--- /dev/null
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/doc/Navbar.java
@@ -0,0 +1,76 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed 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 com.google.gitiles.doc;
+
+import com.google.gitiles.GitilesView;
+import com.google.gitiles.doc.html.HtmlBuilder;
+import com.google.template.soy.shared.restricted.Sanitizers;
+
+import org.pegdown.ast.HeaderNode;
+import org.pegdown.ast.Node;
+import org.pegdown.ast.ReferenceNode;
+import org.pegdown.ast.RootNode;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+class Navbar {
+  static Map<String, Object> bannerSoyData(GitilesView view, RootNode nav) {
+    Map<String, Object> data = new HashMap<>();
+    data.put("siteTitle", null);
+    data.put("logoUrl", null);
+    data.put("homeUrl", null);
+
+    if (nav == null) {
+      return data;
+    }
+
+    for (Iterator<Node> i = nav.getChildren().iterator(); i.hasNext();) {
+      Node n = i.next();
+      if (n instanceof HeaderNode) {
+        HeaderNode h = (HeaderNode) n;
+        if (h.getLevel() == 1) {
+          data.put("siteTitle", MarkdownHelper.getInnerText(h));
+          i.remove();
+          break;
+        }
+      }
+    }
+
+    for (ReferenceNode r : nav.getReferences()) {
+      String key = MarkdownHelper.getInnerText(r);
+      String url = r.getUrl();
+      if ("logo".equalsIgnoreCase(key)) {
+        Object src;
+        if (HtmlBuilder.isImageDataUri(url)) {
+          src = Sanitizers.filterImageDataUri(url);
+        } else {
+          src = url;
+        }
+        data.put("logoUrl", src);
+      } else if ("home".equalsIgnoreCase(key)) {
+        if (MarkdownHelper.isAbsolutePathToMarkdown(url)) {
+          url = GitilesView.doc().copyFrom(view).setPathPart(url).toUrl();
+        }
+        data.put("homeUrl", url);
+      }
+    }
+    return data;
+  }
+
+  private Navbar() {
+  }
+}