// © 2009–2010 EPFL/LAMP // code by Gilles Dubochet with contributions by Johannes Rudolph, "spiros" and Marcin Kubala var topLevelTemplates = undefined; var topLevelPackages = undefined; var scheduler = undefined; var kindFilterState = undefined; var focusFilterState = undefined; var title = $(document).attr('title'); var lastFragment = ""; $(document).ready(function() { $('body').layout({ west__size: '20%', center__maskContents: true }); $('#browser').layout({ center__paneSelector: ".ui-west-center" //,center__initClosed:true ,north__paneSelector: ".ui-west-north" }); $('iframe').bind("load", function(){ try { var subtitle = $(this).contents().find('title').text(); $(document).attr('title', (title ? title + " - " : "") + subtitle); } catch (e) { // Chrome doesn't allow reading the iframe's contents when // used on the local file system. } setUrlFragmentFromFrameSrc(); }); // workaround for IE's iframe sizing lack of smartness if($.browser.msie) { function fixIFrame() { $('iframe').height($(window).height() ) } $('iframe').bind("load",fixIFrame) $('iframe').bind("resize",fixIFrame) } scheduler = new Scheduler(); scheduler.addLabel("init", 1); scheduler.addLabel("focus", 2); scheduler.addLabel("filter", 4); prepareEntityList(); configureTextFilter(); configureKindFilter(); configureEntityList(); setFrameSrcFromUrlFragment(); // If the url fragment changes, adjust the src of iframe "template". $(window).bind('hashchange', function() { if(lastFragment != window.location.hash) { lastFragment = window.location.hash; setFrameSrcFromUrlFragment(); } }); }); // Set the iframe's src according to the fragment of the current url. // fragment = "#scala.Either" => iframe url = "scala/Either.html" // fragment = "#scala.Either@isRight:Boolean" => iframe url = "scala/Either.html#isRight:Boolean" // fragment = "#scalaz.iteratee.package@>@>[E,A]=scalaz.iteratee.package.Iteratee[E,A]" => iframe url = "scalaz/iteratee/package.html#>@>[E,A]=scalaz.iteratee.package.Iteratee[E,A]" function setFrameSrcFromUrlFragment() { function extractLoc(fragment) { var loc = fragment.split('@')[0].replace(/\./g, "/"); if (loc.indexOf(".html") < 0) { loc += ".html"; } return loc; } function extractMemberSig(fragment) { var splitIdx = fragment.indexOf('@'); if (splitIdx < 0) { return; } return fragment.substr(splitIdx + 1); } var fragment = location.hash.slice(1); if (fragment) { var locWithMemeberSig = extractLoc(fragment); var memberSig = extractMemberSig(fragment); if (memberSig) { locWithMemeberSig += "#" + memberSig; } frames["template"].location.replace(location.protocol + locWithMemeberSig); } else { console.log("empty fragment detected"); frames["template"].location.replace("package.html"); } } // Set the url fragment according to the src of the iframe "template". // iframe url = "scala/Either.html" => url fragment = "#scala.Either" // iframe url = "scala/Either.html#isRight:Boolean" => url fragment = "#scala.Either@isRight:Boolean" // iframe url = "scalaz/iteratee/package.html#>@>[E,A]=scalaz.iteratee.package.Iteratee[E,A]" => fragment = "#scalaz.iteratee.package@>@>[E,A]=scalaz.iteratee.package.Iteratee[E,A]" function setUrlFragmentFromFrameSrc() { try { var commonLength = location.pathname.lastIndexOf("/"); var frameLocation = frames["template"].location; var relativePath = frameLocation.pathname.slice(commonLength + 1); if(!relativePath || frameLocation.pathname.indexOf("/") < 0) return; // Add #, remove ".html" and replace "/" with "." fragment = "#" + relativePath.replace(/\.html$/, "").replace(/\//g, "."); // Add the frame's hash after an @ if(frameLocation.hash) fragment += ("@" + frameLocation.hash.slice(1)); // Use replace to not add history items lastFragment = fragment; location.replace(fragment); } catch(e) { // Chrome doesn't allow reading the iframe's location when // used on the local file system. } } var Index = {}; (function (ns) { function openLink(t, type) { var href; if (type == 'object') { href = t['object']; } else { href = t['class'] || t['trait'] || t['case class'] || t['type']; } return [ '' ].join(''); } function createPackageHeader(pack) { return [ '
  • ', 'focushide', '', pack, '
  • ' ].join(''); }; function createListItem(template) { var inner = ''; if (template.object) { inner += openLink(template, 'object'); } if (template['class'] || template['trait'] || template['case class'] || template['type']) { inner += (inner == '') ? '
    ' : ''; inner += openLink(template, template['trait'] ? 'trait' : template['type'] ? 'type' : 'class'); } else { inner += '
    '; } return [ '
  • ', inner, '', template.name.replace(/^.*\./, ''), '
  • ' ].join(''); } ns.createPackageTree = function (pack, matched, focused) { var html = $.map(matched, function (child, i) { return createListItem(child); }).join(''); var header; if (focused && pack == focused) { header = ''; } else { header = createPackageHeader(pack); } return [ '
      ', header, '
        ', html, '
    ' ].join(''); } ns.keys = function (obj) { var result = []; var key; for (key in obj) { result.push(key); } return result; } var hiddenPackages = {}; function subPackages(pack) { return $.grep($('#tpl ol.packages'), function (element, index) { var pack = $('li.pack > .tplshow', element).text(); return pack.indexOf(pack + '.') == 0; }); } ns.hidePackage = function (ol) { var selected = $('li.pack > .tplshow', ol).text(); hiddenPackages[selected] = true; $('ol.templates', ol).hide(); $.each(subPackages(selected), function (index, element) { $(element).hide(); }); } ns.showPackage = function (ol, state) { var selected = $('li.pack > .tplshow', ol).text(); hiddenPackages[selected] = false; $('ol.templates', ol).show(); $.each(subPackages(selected), function (index, element) { $(element).show(); // When the filter is in "packs" state, // we don't want to show the `.templates` var key = $('li.pack > .tplshow', element).text(); if (hiddenPackages[key] || state == 'packs') { $('ol.templates', element).hide(); } }); } })(Index); function configureEntityList() { kindFilterSync(); configureHideFilter(); configureFocusFilter(); textFilter(); } /* 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 topLevelPackages) to serve as reference for resetting the list when needed. Be advised: this function should only be called once, on page load. */ function prepareEntityList() { var classIcon = $("#library > img.class"); var traitIcon = $("#library > img.trait"); var typeIcon = $("#library > img.type"); var objectIcon = $("#library > img.object"); var packageIcon = $("#library > img.package"); $('#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.type", this).each(function() { $(this).replaceWith(typeIcon.clone()); }); $("span.object", this).each(function() { $(this).replaceWith(objectIcon.clone()); }); $("span.package", this).each(function() { $(this).replaceWith(packageIcon.clone()); }); }); $('#tpl li.pack') .prepend("hide") .prepend("focus"); } /* Handles all key presses while scrolling around with keyboard shortcuts in left panel */ function keyboardScrolldownLeftPane() { scheduler.add("init", function() { $("#textfilter input").blur(); var $items = $("#tpl li"); $items.first().addClass('selected'); $(window).bind("keydown", function(e) { var $old = $items.filter('.selected'), $new; switch ( e.keyCode ) { case 9: // tab $old.removeClass('selected'); break; case 13: // enter $old.removeClass('selected'); var $url = $old.children().filter('a:last').attr('href'); $("#template").attr("src",$url); break; case 27: // escape $old.removeClass('selected'); $(window).unbind(e); $("#textfilter input").focus(); break; case 38: // up $new = $old.prev(); if (!$new.length) { $new = $old.parent().prev(); } if ($new.is('ol') && $new.children(':last').is('ol')) { $new = $new.children().children(':last'); } else if ($new.is('ol')) { $new = $new.children(':last'); } break; case 40: // down $new = $old.next(); if (!$new.length) { $new = $old.parent().parent().next(); } if ($new.is('ol')) { $new = $new.children(':first'); } break; } if ($new.is('li')) { $old.removeClass('selected'); $new.addClass('selected'); } else if (e.keyCode == 38) { $(window).unbind(e); $("#textfilter input").focus(); } }); }); } /* Configures the text filter */ function configureTextFilter() { scheduler.add("init", function() { $("#textfilter").append(""); var input = $("#textfilter input"); resizeFilterBlock(); input.bind('keyup', function(event) { if (event.keyCode == 27) { // escape input.attr("value", ""); } if (event.keyCode == 40) { // down arrow $(window).unbind("keydown"); keyboardScrolldownLeftPane(); return false; } textFilter(); }); input.bind('keydown', function(event) { if (event.keyCode == 9) { // tab $("#template").contents().find("#mbrsel-input").focus(); input.attr("value", ""); return false; } textFilter(); }); input.focus(function(event) { input.select(); }); }); scheduler.add("init", function() { $("#textfilter > .post").click(function(){ $("#textfilter input").attr("value", ""); textFilter(); }); }); } function compilePattern(query) { var escaped = query.replace(/([\.\*\+\?\|\(\)\[\]\\])/g, '\\$1'); if (query.toLowerCase() != query) { // Regexp that matches CamelCase subbits: "BiSe" is // "[a-z]*Bi[a-z]*Se" and matches "BitSet", "ABitSet", ... return new RegExp(escaped.replace(/([A-Z])/g,"[a-z]*$1")); } else { // if query is all lower case make a normal case insensitive search return new RegExp(escaped, "i"); } } // Filters all focused templates and packages. This function should be made less-blocking. // @param query The string of the query function textFilter() { var query = $("#textfilter input").attr("value") || ''; var queryRegExp = compilePattern(query); // if we are filtering on types, then we have to display types // ("display packages only" is not possible when filtering) if (query !== "") { kindFilter("all"); } // Three things trigger a reload of the left pane list: // typeof textFilter.lastQuery === "undefined" <-- first load, there is nothing yet in the left pane // textFilter.lastQuery !== query <-- the filter text has changed // focusFilterState != null <-- a package has been "focused" if ((typeof textFilter.lastQuery === "undefined") || (textFilter.lastQuery !== query) || (focusFilterState != null)) { textFilter.lastQuery = query; scheduler.clear("filter"); $('#tpl').html(''); var index = 0; var searchLoop = function () { var packages = Index.keys(Index.PACKAGES).sort(); while (packages[index]) { var pack = packages[index]; var children = Index.PACKAGES[pack]; index++; if (focusFilterState) { if (pack == focusFilterState || pack.indexOf(focusFilterState + '.') == 0) { ; } else { continue; } } var matched = $.grep(children, function (child, i) { return queryRegExp.test(child.name); }); if (matched.length > 0) { $('#tpl').append(Index.createPackageTree(pack, matched, focusFilterState)); scheduler.add('filter', searchLoop); return; } } $('#tpl a.packfocus').click(function () { focusFilter($(this).parent().parent()); }); configureHideFilter(); }; scheduler.add('filter', searchLoop); } } /* Configures the hide tool by adding the hide link to all packages. */ function configureHideFilter() { $('#tpl li.pack a.packhide').click(function () { var packhide = $(this) var action = packhide.text(); var ol = $(this).parent().parent(); if (action == "hide") { Index.hidePackage(ol); packhide.text("show"); } else { Index.showPackage(ol, kindFilterState); packhide.text("hide"); } return false; }); } /* Configures the focus tool by adding the focus bar in the filter box (initially hidden), and by adding the focus link to all packages. */ function configureFocusFilter() { scheduler.add("init", function() { focusFilterState = null; if ($("#focusfilter").length == 0) { $("#filter").append("
    focused on
    "); $("#focusfilter > .focusremove").click(function(event) { textFilter(); $("#focusfilter").hide(); $("#kindfilter").show(); resizeFilterBlock(); focusFilterState = null; }); $("#focusfilter").hide(); resizeFilterBlock(); } }); scheduler.add("init", function() { $('#tpl li.pack a.packfocus').click(function () { focusFilter($(this).parent()); return false; }); }); } /* Focuses the entity index on a specific package. To do so, it will copy the sub-templates and sub-packages of the focuses package into the top-level templates and packages position of the index. The original top-level @param package The
  • element that corresponds to the package in the entity index */ function focusFilter(package) { scheduler.clear("filter"); var currentFocus = $('li.pack > .tplshow', package).text(); $("#focusfilter > .focuscoll").empty(); $("#focusfilter > .focuscoll").append(currentFocus); $("#focusfilter").show(); $("#kindfilter").hide(); resizeFilterBlock(); focusFilterState = currentFocus; kindFilterSync(); textFilter(); } function configureKindFilter() { scheduler.add("init", function() { kindFilterState = "all"; $("#filter").append(""); $("#kindfilter > a").click(function(event) { kindFilter("packs"); }); resizeFilterBlock(); }); } function kindFilter(kind) { if (kind == "packs") { kindFilterState = "packs"; kindFilterSync(); $("#kindfilter > a").replaceWith("display all entities"); $("#kindfilter > a").click(function(event) { kindFilter("all"); }); } else { kindFilterState = "all"; kindFilterSync(); $("#kindfilter > a").replaceWith("display packages only"); $("#kindfilter > a").click(function(event) { kindFilter("packs"); }); } } /* Applies the kind filter. */ function kindFilterSync() { if (kindFilterState == "all" || focusFilterState != null) { $("#tpl a.packhide").text('hide'); $("#tpl ol.templates").show(); } else { $("#tpl a.packhide").text('show'); $("#tpl ol.templates").hide(); } } function resizeFilterBlock() { $("#tpl").css("top", $("#filter").outerHeight(true)); }