It's possible to do what you want using JavaScript in the Free Nodelet. Just add the code below to your Free Nodelet and make sure your Free Nodelet is visible.
There's a delay between the time the link is clicked and when the browser loads the desired page because an extra HTTP fetch is done in the background to determine the id of the desired node.
Works in Firefox. Untested elsewhere.
<script type="text/javascript">
// ========================================
// Adds parent and root links next to nodes in
// "Perl Monks User Search". Links are added even
// when the node is a root node.
//
// When a parent or root link is clicked, the id of the
// desired node is fetched using an asynchronous HTTP
// request.
//
// When the id of the desired node is known, the browser
// is directed to load the desired node.
//
// Should the node be a root node, the browser is directed
// to load the node whose parent/root was requested.
var redir_requester = null;
function init_redir_asynch_request_obj() {
if (redir_requester != null
&& redir_requester.readyState != 0
&& redir_requester.readyState != 4
) {
redir_requester.abort();
}
redir_requester = null;
try {
redir_requester = new XMLHttpRequest();
} catch (error) {
try {
redir_requester = new ActiveXObject("Microsoft.XMLHTTP");
} catch (error) {
}
}
return redir_requester;
}
function _goto_by_field(requester, id, field) {
if (requester.readyState != 4)
return;
if (requester.status != 200) {
// XXX Need to return feedback to the user.
return;
}
var doc = requester.responseXML;
var query = "/node/data/field`[@name='" + field + "']";
var dest_id_value = doc
.evaluate(query, doc, null, XPathResult.ANY_UNO
+RDERED_NODE_TYPE, null)
.singleNodeValue;
var dest_id = (dest_id_value
? dest_id_value.textContent
: id
);
window.location = '/?node_id=' + dest_id;
}
function goto_by_field(id, field) {
var requester = init_redir_asynch_request_obj();
requester.onreadystatechange =
function () { _goto_by_field(requester, id, field) };
requester.open('GET', '/?displaytype=xml;node_id=' + id);
requester.send(null);
// XXX Need to return feedback to the user.
}
function goto_parent_of(id) { goto_by_field(id, 'parent_node'); }
function goto_root_of(id) { goto_by_field(id, 'root_node' ); }
function show_ancestors_user_search() {
// if (decodeURIComponent('`title%`') != 'Perl Monks User Search')
// return;
if ('`title%`' != 'Perl%20Monks%20User%20Search')
return;
var writeups = document.getElementById('writeups');
// We need to flatten the collection returned by
// getElementsByTagName into an array because we
// will be adding more "a" elements.
var anchs = new Array(0);
for each (var anch in writeups.getElementsByTagName('a')) {
if (anch.nodeType != Node.ELEMENT_NODE)
continue;
anchs.push(anch);
}
// Add parent and root links to every "a" element we found.
for each (var anch in anchs) {
var id = anch.href.match(/\d+$/);
var container = document.createElement('span');
container.innerHTML = (''
+ ' '
+ '<a href="javascript:goto_parent_of(' + id + ')" class="par
+ent_link">`[parent]</a>'
+ ' '
+ '<a href="javascript:goto_root_of(' + id + ')" class="root_
+link">`[root]</a>'
);
anch.parentNode.insertBefore(container, anch.nextSibling);
}
}
// ========================================
// Execute our snippets after the page is rendered.
var old_onload = window.onload;
window.onload = function() {
if (old_onload != null)
old_onload();
show_ancestors_user_search();
// ...
};
</script>
Update: Removed text that got accidently pasted in, as pointed by shmem.