// Enable expanding/folding folders in TracBrowser (function($){ var FOLDERID_COUNTER = 0; var SUBFOLDER_INDENT = 20; // enableExpandDir adds the capability to folder rows to be expanded and folded // It also teach the rows about their ancestors. It expects: // - `parent_tr`, the logical parent row (`null` if there's no ancestor) // - a `rows` jQuery object matching the newly created entry rows // - `qargs`, additional parameters to send to the server when expanding window.enableExpandDir = function(parent_tr, rows, qargs) { // the ancestors folder ids are present in the parent_tr class attribute var ancestor_folderids = []; if (parent_tr) ancestor_folderids = $.grep(parent_tr.attr("class").split(" "), function(c) { return c.match(/^f\d+$/)}); rows.each(function () { var a = $(this).find("a.dir"); if (a.length) { // then the entry is a folder // create new folder id var folderid = "f" + FOLDERID_COUNTER++; this.id = folderid; $(this).addClass(folderid); // add the expander icon a.wrap('
'); var expander = a.before(' ').prev(); expander.attr("title", "Expand sub-directory in place") .click(function() { toggleDir($(this), qargs); }); } // tie that row to ancestor folders if (parent_tr) $(this).addClass(ancestor_folderids.join(" ")); }); } // handler for click event on the expander icons window.toggleDir = function(expander, qargs) { var tr = expander.parents("tr"); var folderid = tr.get(0).id; if ( tr.filter(".expanded").length ) { // then *fold* tr.removeClass("expanded").addClass("collapsed"); tr.siblings("tr."+folderid).hide(); expander.attr("title", "Re-expand directory"); return; } if ( tr.filter(".collapsed").length ) { // then *expand* tr.removeClass("collapsed").addClass("expanded"); tr.siblings("tr."+folderid).show(); // Note that the above will show all the already fetched subtree, // so we have to fold again the folders which were already collapsed. tr.siblings("tr.collapsed").each(function() { tr.siblings("tr."+this.id).not(this).hide(); }); } else { // then *fetch* var td = expander.parents("td"); var td_class = td.attr("class"); var a = expander.next("a"); var depth = parseFloat(td.css("padding-left").replace(/^(\d*\.\d*).*$/, "$1")) + SUBFOLDER_INDENT; tr.addClass("expanded"); // insert "Loading ..." row tr.after(''); var loading_row = tr.next(); loading_row.children("td").addClass(td_class) .attr("colspan", tr.children("td").length) .css("padding-left", depth); loading_row.find("span.loading").text("Loading " + a.text() + "..."); // XHR for getting the rows corresponding to the folder entries $.ajax({ type: "GET", url: a.attr("href"), data: qargs, dataType: "html", success: function(data) { // Safari 3.1.1 has some trouble reconstructing HTML snippets // bigger than 50k - splitting in rows before building DOM nodes var rows = data.replace(/^]+>/, "").split(""); if (rows.length) { // insert entry rows $(rows).each(function() { row = $(this+""); row.children("td."+td_class).css("padding-left", depth); // make all entry rows collapsible but only subdir rows expandable enableExpandDir(tr, row, qargs); loading_row.before(row); }); // remove "Loading ..." row loading_row.remove(); } else { loading_row.find("span.loading").text("").append("(empty)") .removeClass("loading"); // make the (empty) row collapsible enableExpandDir(tr, loading_row, qargs); } }, error: function(req, err, exc) { loading_row.find("span.loading").text("").append("(error)") .removeClass("loading"); enableExpandDir(tr, loading_row, qargs); } }); } expander.attr("title", "Fold directory"); } })(jQuery);