diff --git a/project/Docs.scala b/project/Docs.scala index 42f4d4303..68e459f8b 100644 --- a/project/Docs.scala +++ b/project/Docs.scala @@ -12,10 +12,16 @@ object Docs { val cnameFile = SettingKey[File]("cname-file", "Location of the CNAME file for the website.") + val SnapshotPath = "snapshot" + val ReleasePath = "release" + val DocsPath = "docs" + val IndexHtml = "index.html" + val HomeHtml = "home.html" + val VersionPattern = """(\d+)\.(\d+)\.(\d+)(-.+)?""".r.pattern def settings: Seq[Setting[_]] = site.settings ++ - site.sphinxSupport("docs") ++ + site.sphinxSupport(DocsPath) ++ site.includeScaladoc("api") ++ siteIncludeSxr("sxr") ++ ghPagesSettings @@ -41,10 +47,25 @@ object Docs IO.copy(mappings map { case (file, target) => (file, versioned / target) }) IO.copyFile(cname, repo / cname.getName) IO.touch(repo / ".nojekyll") - linkSite(repo, v, if(snap) "snapshot" else "release", s.log) + IO.write(repo / "versions.js", versionsJs(sortVersions(collectVersions(repo)))) + if(!snap) + RootIndex(versioned / DocsPath / "home.html", repo / IndexHtml) + linkSite(repo, v, if(snap) SnapshotPath else ReleasePath, s.log) s.log.info("Copied site to " + versioned) repo } + def versionsJs(vs: Seq[String]): String = "var availableDocumentationVersions = " + vs.mkString("['", "', '", "']") + // names of all directories that are explicit versions + def collectVersions(base: File): Seq[String] = (base * versionFilter).get.map(_.getName) + def sortVersions(vs: Seq[String]): Seq[String] = vs.sortBy(versionComponents).reverse + def versionComponents(v: String): Option[(Int,Int,Int,Option[String])] = { + val m = VersionPattern.matcher(v) + if(m.matches()) + Some( (m.group(1).toInt, m.group(2).toInt, m.group(3).toInt, Option(m.group(4))) ) + else + None + } + def versionFilter = new PatternFilter(VersionPattern) && DirectoryFilter def linkSite(base: File, to: String, from: String, log: Logger) { val current = base / to @@ -60,4 +81,42 @@ object Docs case 0 => () case code => error("Could not create symbolic link '" + file.getAbsolutePath + "' with path " + path) } +} +object RootIndex +{ + import Docs._ + import org.jsoup._ + + def apply(versionIndex: File, to: File) + { + val doc = Jsoup.parse(versionIndex, "UTF-8") + rewriteLinks(doc) + removeSearch(doc) + IO.write(to, doc.outerHtml) + } + def retargetIndexLink(original: String): String = + if(isAbsolute(original) || original.startsWith("#")) + original + else + ReleasePath + "/docs/" + original + + def isAbsolute(s: String): Boolean = (new java.net.URI(s)).isAbsolute + + def rewriteLinks(doc: nodes.Document) + { + rewriteLinks(doc, "*", "href") + rewriteLinks(doc, "script", "src") + } + def rewriteLinks(doc: nodes.Document, elemName: String, attrName: String): Unit = + for(elem <- select(doc, elemName + "[" + attrName + "]")) + elem.attr(attrName, retargetIndexLink(elem.attr(attrName))) + + def removeSearch(doc: nodes.Document): Unit = + doc.select(".search").remove() + + def select(doc: nodes.Document, s: String) = + { + import collection.JavaConverters._ + doc.select(s).iterator.asScala + } } \ No newline at end of file diff --git a/project/p.sbt b/project/p.sbt index d40b6fca8..7e437e8f5 100644 --- a/project/p.sbt +++ b/project/p.sbt @@ -1,4 +1,7 @@ -libraryDependencies += "net.databinder" %% "dispatch-http" % "0.8.8" +libraryDependencies ++= Seq( + "net.databinder" %% "dispatch-http" % "0.8.8", + "org.jsoup" % "jsoup" % "1.7.1" +) addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.6.0") diff --git a/src/sphinx/_sphinx/themes/sbt/layout.html b/src/sphinx/_sphinx/themes/sbt/layout.html index 083307619..453f8467f 100644 --- a/src/sphinx/_sphinx/themes/sbt/layout.html +++ b/src/sphinx/_sphinx/themes/sbt/layout.html @@ -6,6 +6,8 @@ {% extends "basic/layout.html" %} +{% set script_files = script_files + ['../../versions.js'] %} +{% set script_files = script_files + ['_static/set-versions.js'] %} {% set css_files = css_files + ['_static/base.css'] %} {% set css_files = css_files + ['_static/docs.css'] %} @@ -73,7 +75,7 @@ {%- endif %}
  • - Version {{ release|e }} +
  • @@ -99,5 +101,3 @@ {% block footer %}{% endblock %} {% endblock %} - - diff --git a/src/sphinx/_sphinx/themes/sbt/static/docs.css b/src/sphinx/_sphinx/themes/sbt/static/docs.css index 98f81196b..710eedb71 100644 --- a/src/sphinx/_sphinx/themes/sbt/static/docs.css +++ b/src/sphinx/_sphinx/themes/sbt/static/docs.css @@ -208,3 +208,15 @@ input#submit-search { input#search-input { margin: 0px; } +.breadcrumb li select { + width: auto; + height: auto; + background-color: rgba(242, 242, 235, 0.1); + border: 1px solid rgba(204, 204, 204, 0.5); + padding: 1px; + margin: -2px 0px 0px; + font-weight: bold; +} +.breadcrumb li select option { + font-weight: normal; +} \ No newline at end of file diff --git a/src/sphinx/_sphinx/themes/sbt/static/set-versions.js b/src/sphinx/_sphinx/themes/sbt/static/set-versions.js new file mode 100644 index 000000000..09fac8742 --- /dev/null +++ b/src/sphinx/_sphinx/themes/sbt/static/set-versions.js @@ -0,0 +1,60 @@ +$(document).ready(function() { + // array of versions provided by short js file in root so that + // old documentation versions do not need to be + var versions = availableDocumentationVersions; + var releasePath = 'release/'; + var snapshotPath = 'snapshot/'; + var docsPath = "docs/"; + + // get the version drop-down + var select = $("#versions"); + // the currently selected value is the current version + var selected = select.val(); + // clear the options, which should only include the current version + select.html(''); + + // populate the options with the latest list of versions + for(var i = 0; i < versions.length; i++) { + var v = versions[i]; + var sel = ''; + if (v == selected) sel = 'selected '; + select.append(''); + } + + // check if primary exists, go there if it does, or go to fallback if it does not + var gotoIfExists = function(primary, fallback) { + $.ajax({ + type: 'HEAD', + url: primary, + success: function() { document.location.href = primary }, + error: function() { document.location.href = fallback }, + }); + }; + + // return a new URL String with its path transformed by function f: String => String + var mapPath = function(urlString, f) { + var u = document.createElement('a'); + u.href = urlString; + u.pathname = f(u.pathname); + return u.href; + }; + + // when an option is selected, switch to that version of the current page, + // but if it doesn't exist, go to the index for that version + select.change(function() { + var newV = $(this).val(); + var oldLoc = document.location.href; + + var changeVersion = function(oldPathname) { + var newPath = newV + '/'; + var changed = oldPathname.replace(selected + '/', newPath).replace(snapshotPath, newPath).replace(releasePath, newPath); + // This occurs for unversioned files, specifically /index.html. + // Redirect to the versioned path in this case (won't work when previewing on the local filesytem) + if (changed == oldPathname) changed = newPath + docsPath + changed; + return changed; + }; + var home = function(pathname) { return 'index.html'; }; + + gotoIfExists( mapPath(oldLoc, changeVersion), mapPath(oldLoc, home)); + }); +});