diff options
author | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2010-10-27 11:58:31 +0000 |
---|---|---|
committer | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2010-10-27 11:58:31 +0000 |
commit | d96113b2bf38b5e5f817d79ce7b5e24394bb7918 (patch) | |
tree | 3802b633ee524e3bca3526275e3db8ded4340f74 | |
parent | 0e3e701870e705a453df5f4e178af377eeb54aec (diff) | |
download | scala-d96113b2bf38b5e5f817d79ce7b5e24394bb7918.tar.gz scala-d96113b2bf38b5e5f817d79ce7b5e24394bb7918.tar.bz2 scala-d96113b2bf38b5e5f817d79ce7b5e24394bb7918.zip |
[scaladoc] Even faster JavaScript index filteri...
[scaladoc] Even faster JavaScript index filtering, by reducing DOM
manipulations. Contributed by Kato Kazuyoshi. Review by dubochet.
-rw-r--r-- | src/compiler/scala/tools/nsc/doc/html/page/Index.scala | 51 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js | 170 |
2 files changed, 127 insertions, 94 deletions
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala index 9a964ee30f..8b50aca85c 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala @@ -12,6 +12,7 @@ import model._ import scala.collection._ import scala.xml._ +import scala.util.parsing.json.{JSONObject, JSONArray} class Index(universe: Universe, indexModel: IndexModelFactory#IndexModel) extends HtmlPage { @@ -118,11 +119,7 @@ class Index(universe: Universe, indexModel: IndexModelFactory#IndexModel) extend placeholderSeq ++ createLink(entry, includePlaceholder = false, includeText = true) } - <li id={ - "template-" + toId(entities.head.qualifiedName) - } title={ entities.head.qualifiedName }>{ - itemContents - }</li> + <li title={ entities.head.qualifiedName }>{ itemContents }</li> } }</ol> <ol class="packages"> { @@ -145,20 +142,40 @@ class Index(universe: Universe, indexModel: IndexModelFactory#IndexModel) extend }) } + def mergeByQualifiedName(source: List[DocTemplateEntity]): Map[String, List[DocTemplateEntity]]= { + var result = Map[String, List[DocTemplateEntity]]() + + for (t <- source) { + val k = t.qualifiedName + result += k -> (result.getOrElse(k, List()) :+ t) + } + + result + } + def scriptElement = { - val templatesOf = allPackagesWithTemplates - - val elements = templatesOf.keys.map(pack => { - List( - "{ name: '", pack, "', children: ", - templatesOf(pack).map(t => "'" + t + "'").mkString("[", ",", "]"), - "}" - ).mkString("") - }) + val packages = allPackagesWithTemplates.toIterable.map(_ match { + case (pack, templates) => { + val merged = mergeByQualifiedName(templates) + + val ary = merged.keys.toList.sortBy(_.toLowerCase).map(key => { + val pairs = merged(key).map( + t => docEntityKindToString(t) -> relativeLinkTo(t) + ) :+ ("name" -> key) + + JSONObject(scala.collection.immutable.Map(pairs : _*)) + }) + + pack.qualifiedName -> JSONArray(ary) + } + }).toSeq + + val obj = + JSONObject(scala.collection.immutable.Map(packages : _*)).toString() - <script type="text/javascript">{ - elements.mkString("Index.PACKAGES = [", ",", "]") - }</script> + <script type="text/javascript"> + Index.PACKAGES = {scala.xml.Unparsed(obj)}; + </script> } def allPackagesWithTemplates: Map[Package, List[DocTemplateEntity]] = { diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js index 6167fab297..9fd427470a 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js @@ -5,7 +5,6 @@ var topLevelTemplates = undefined; var topLevelPackages = undefined; var scheduler = undefined; -var domCache = undefined; var kindFilterState = undefined; var focusFilterState = undefined; @@ -49,9 +48,6 @@ $(document).ready(function() { } } - domCache = new DomCache(); - domCache.update(); - prepareEntityList(); configureTextFilter(); @@ -69,13 +65,52 @@ var Index = {}; }); } - ns.idOfTemplate = function (name) { - return 'template-' + toId(name); - } - ns.idOfPackage = function (name) { return 'package-' + toId(name); } + + function openLink(t, type) { + var href; + if (type == 'object') { + href = t['object']; + } else { + href = t['class'] || t['trait'] || t['case class']; + } + return [ + '<a class="tplshow" target="template" href="', + href, + '"><img width="13" height="13" class="', + type, + ' icon" src="lib/', + type, + '.png" />' + ].join(''); + } + + ns.createListItem = function (template) { + var inner = ''; + + + if (template.object) { + inner += openLink(template, 'object'); + } + + if (template['class'] || template['trait'] || template['case class']) { + inner += (inner == '') ? + '<div class="placeholder" />' : '</a>'; + inner += openLink(template, template['trait'] ? 'trait' : 'class'); + } else { + inner += '<div class="placeholder"/>'; + } + + return [ + '<li>', + inner, + '<span class="tplLink">', + template.name.replace(/^.*\./, ''), + '</span></a></li>' + ].join(''); + } })(Index); function configureEntityList() { @@ -85,19 +120,6 @@ function configureEntityList() { textFilter(); } -/* The DomCache class holds a series of pointers to interesting parts of the page's DOM tree. Generally, any DOM - accessor should be reduced to the context of a relevant entity from the cache. This is crucial to maintaining - decent performance of the page. */ -function DomCache() { - var cache = this; - this.packs = undefined; - this.liPacks = undefined; - this.update = function() { - cache.packs = $(".pack"); - cache.liPacks = cache.packs.filter("li"); - } -} - /* Updates the list of entities (i.e. the content of the #tpl element) from the raw form generated by Scaladoc to a form suitable for display. In particular, it adds class and object etc. icons, and it configures links to open in the right frame. Furthermore, it sets the two reference top-level entities lists (topLevelTemplates and @@ -108,18 +130,17 @@ function prepareEntityList() { var traitIcon = $("#library > img.trait"); var objectIcon = $("#library > img.object"); var packageIcon = $("#library > img.package"); - scheduler.addForAll("init", domCache.packs, function(pack) { - var packTemplates = $("> ol.templates > li", pack); - $("> h3 > a.tplshow", pack).add("> a.tplshow", packTemplates).attr("target", "template"); - $("span.class", packTemplates).each(function() { $(this).replaceWith(classIcon.clone()); }); - $("span.trait", packTemplates).each(function() { $(this).replaceWith(traitIcon.clone()); }); - $("span.object", packTemplates).each(function() { $(this).replaceWith(objectIcon.clone()); }); - $("span.package", packTemplates).each(function() { $(this).replaceWith(packageIcon.clone()); }); - }); - scheduler.add("init", function() { - topLevelTemplates = $("#tpl > ol.templates").clone(); - topLevelPackages = $("#tpl > ol.packages").clone(); + + $('#tpl li.pack > a.tplshow').attr("target", "template"); + $('#tpl li.pack').each(function () { + $("span.class", this).each(function() { $(this).replaceWith(classIcon.clone()); }); + $("span.trait", this).each(function() { $(this).replaceWith(traitIcon.clone()); }); + $("span.object", this).each(function() { $(this).replaceWith(objectIcon.clone()); }); + $("span.package", this).each(function() { $(this).replaceWith(packageIcon.clone()); }); }); + $('#tpl li.pack') + .prepend("<a class='packhide'>hide</a>") + .prepend("<a class='packfocus'>focus</a>"); } /* Configures the text filter */ @@ -160,33 +181,25 @@ function textFilter() { queryRegExp = new RegExp(query, "i"); } - $.each(Index.PACKAGES, function (i, package) { - var empty = true; - var matchedSet = {}; - - $.each(package.children, function (j, child) { - if (queryRegExp.test(child)) { - matchedSet[child] = 1; - empty = false; - } + $.each(Index.PACKAGES, function (package, children) { + var matched = $.grep(children, function (child, i) { + return queryRegExp.test(child.name); }); - var pack = $('#' + Index.idOfPackage(package.name)); - if (empty) { + var pack = $('#' + Index.idOfPackage(package)); + if (matched.length == 0) { $("> h3", pack).hide(); - $("> .templates", pack).hide(); $("> .packhide", pack).hide(); $("> .packfocus", pack).hide(); - + $("> .templates", pack).html(''); return; } + var html = $.map(matched, function (child, i) { + return Index.createListItem(child); + }).join(''); $("> h3", pack).show(); - $.each(package.children, function (j, child) { - $('#' + Index.idOfTemplate(child))[ - matchedSet[child] ? 'show' : 'hide' - ](); - }); + $("> .templates", pack).html(html); if(kindFilterState=="all") $("> .templates", pack).show(); $("> .packhide", pack).show(); $("> .packfocus", pack).show(); @@ -196,23 +209,20 @@ function textFilter() { /* Configures the hide tool by adding the hide link to all packages. */ function configureHideFilter() { - scheduler.addForAll("init", domCache.liPacks, function(pack) { - $(pack).prepend("<a class='packhide'>hide</a>"); - $("> a.packhide", pack).click(function(event) { - var packhide = $(this) - var action = packhide.text(); - if (action == "hide") { - $("~ ol", packhide).hide(); - packhide.text("show"); - } - else { - // When the filter is in "packs" state, we don't want to show the `.templates`, only `.packages` - var selector = kindFilterState=="packs" ? "~ ol.packages" : "~ ol" - $(selector, packhide).show(); - packhide.text("hide"); - } - return false; - }); + $('#tpl li.pack a.packhide').click(function () { + var packhide = $(this) + var action = packhide.text(); + if (action == "hide") { + $("~ ol", packhide).hide(); + packhide.text("show"); + } + else { + // When the filter is in "packs" state, we don't want to show the `.templates`, only `.packages` + var selector = kindFilterState=="packs" ? "~ ol.packages" : "~ ol" + $(selector, packhide).show(); + packhide.text("hide"); + } + return false; }); } @@ -227,8 +237,9 @@ function configureFocusFilter() { scheduler.clear("filter"); scheduler.add("focus", function() { $("#tpl > ol.templates").replaceWith(topLevelTemplates.clone()); + topLevelTemplates = undefined; $("#tpl > ol.packages").replaceWith(topLevelPackages.clone()); - domCache.update(); + topLevelPackages = undefined; $("#focusfilter").hide(); $("#kindfilter").show(); resizeFilterBlock(); @@ -240,9 +251,8 @@ function configureFocusFilter() { resizeFilterBlock(); } }); - scheduler.addForAll("init", domCache.liPacks, function(pack) { - $(pack).prepend("<a class='packfocus'>focus</a>"); - $("> a.packfocus", pack).click(function(event) { + scheduler.add("init", function() { + $('#tpl li.pack a.packfocus').click(function () { focusFilter($(this).parent()); return false; }); @@ -258,11 +268,18 @@ function focusFilter(package) { var currentFocus = package.attr("title"); $("#focusfilter > .focuscoll").empty(); $("#focusfilter > .focuscoll").append(currentFocus); + + if (! topLevelTemplates) { + topLevelTemplates = $("#tpl > ol.templates").clone(); + } + if (! topLevelPackages) { + topLevelPackages = $("#tpl > ol.packages").clone(); + } + var packTemplates = $("> ol.templates", package); var packPackages = $("> ol.packages", package); $("#tpl > ol.templates").replaceWith(packTemplates); $("#tpl > ol.packages").replaceWith(packPackages); - domCache.update(); $("#focusfilter").show(); $("#kindfilter").hide(); resizeFilterBlock(); @@ -299,14 +316,13 @@ function kindFilter(kind) { function kindFilterSync() { scheduler.add("kind", function () { if (kindFilterState == "all" || focusFilterState != null) - scheduler.addForAll("kind", domCache.packs, function(pack0) { - $("> ol.templates", pack0).show(); + scheduler.add("kind", function() { + $("#tpl h3 + ol.templates").show(); }); else - scheduler.addForAll("kind", domCache.packs, function(pack0) { - $("> ol.templates", pack0).hide(); + scheduler.add("kind", function() { + $("#tpl h3 + ol.templates").hide(); }); - textFilter(); }); } |