Added quick-and-dirty javascript dropdown search thing
This commit is contained in:
parent
10e8d4acae
commit
4d699d8d47
3 changed files with 234 additions and 3 deletions
|
|
@ -475,10 +475,11 @@ sub xmlsearch {
|
||||||
my $q = $self->reqGet('q')||'';
|
my $q = $self->reqGet('q')||'';
|
||||||
my $man = $self->dbSearch($q, 20);
|
my $man = $self->dbSearch($q, 20);
|
||||||
|
|
||||||
|
# The JS dropdown search expects this particular format.
|
||||||
$self->resHeader('Content-Type' => 'text/xml; charset=UTF-8');
|
$self->resHeader('Content-Type' => 'text/xml; charset=UTF-8');
|
||||||
xml;
|
xml;
|
||||||
tag 'results';
|
tag 'results';
|
||||||
tag 'man', %$_, undef for(@$man);
|
tag 'item', id => "$_->{name}.$_->{section}", %$_, undef for(@$man);
|
||||||
end 'results';
|
end 'results';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -501,8 +502,8 @@ sub htmlHeader {
|
||||||
|
|
||||||
div id => 'header';
|
div id => 'header';
|
||||||
a href => '/', 'manned.org';
|
a href => '/', 'manned.org';
|
||||||
form;
|
form action => '/', method => 'get';
|
||||||
input type => 'text', name => 'q';
|
input type => 'text', name => 'q', id => 'q';
|
||||||
input type => 'submit', value => ' ';
|
input type => 'submit', value => ' ';
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
|
||||||
|
|
@ -80,3 +80,11 @@ td { padding: 1px 5px; font-size: 12px; border-left: 1px solid #ccc; }
|
||||||
pre, pre * { font-family: "Lucida Console", Monospace; font-size: 15px }
|
pre, pre * { font-family: "Lucida Console", Monospace; font-size: 15px }
|
||||||
pre b { color: #369; font-weight: normal; }
|
pre b { color: #369; font-weight: normal; }
|
||||||
|
|
||||||
|
#ds_box { position: absolute; top: 0; border: 1px solid $border$; border-top: none; background: #f0f8ff; cursor: pointer; z-index: 2 }
|
||||||
|
#ds_box.hidden { display: none }
|
||||||
|
#ds_box b { padding: 2px 0 0 10px; font-size: 12px; }
|
||||||
|
#ds_box tr.selected { background: #fff; }
|
||||||
|
#ds_box table { width: 100%; border: 0; background: none; margin: 0 }
|
||||||
|
#ds_box td { border: 0; padding: 1px 5px }
|
||||||
|
#ds_box i { padding-left: 5px; color: #aaa; font-style: normal }
|
||||||
|
|
||||||
|
|
|
||||||
222
www/man.js
222
www/man.js
|
|
@ -3,6 +3,27 @@
|
||||||
var expanded_icon = '▾';
|
var expanded_icon = '▾';
|
||||||
var collapsed_icon = '▸';
|
var collapsed_icon = '▸';
|
||||||
|
|
||||||
|
var http_request = false;
|
||||||
|
function ajax(url, func, async) {
|
||||||
|
if(!async && http_request)
|
||||||
|
http_request.abort();
|
||||||
|
var req = (window.ActiveXObject) ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();
|
||||||
|
if(req == null)
|
||||||
|
return alert("Your browser does not support the functionality this website requires.");
|
||||||
|
if(!async)
|
||||||
|
http_request = req;
|
||||||
|
req.onreadystatechange = function() {
|
||||||
|
if(!req || req.readyState != 4 || !req.responseText)
|
||||||
|
return;
|
||||||
|
if(req.status != 200)
|
||||||
|
return alert('Whoops, error! :(');
|
||||||
|
func(req);
|
||||||
|
};
|
||||||
|
url += (url.indexOf('?')>=0 ? ';' : '?')+(Math.floor(Math.random()*999)+1);
|
||||||
|
req.open('GET', url, true);
|
||||||
|
req.send(null);
|
||||||
|
}
|
||||||
|
|
||||||
function byId(n) {
|
function byId(n) {
|
||||||
return document.getElementById(n)
|
return document.getElementById(n)
|
||||||
}
|
}
|
||||||
|
|
@ -49,6 +70,20 @@ function tag() {
|
||||||
}
|
}
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
function addBody(el) {
|
||||||
|
if(document.body.appendChild)
|
||||||
|
document.body.appendChild(el);
|
||||||
|
else if(document.documentElement.appendChild)
|
||||||
|
document.documentElement.appendChild(el);
|
||||||
|
else if(document.appendChild)
|
||||||
|
document.appendChild(el);
|
||||||
|
}
|
||||||
|
function setContent() {
|
||||||
|
setText(arguments[0], '');
|
||||||
|
for(var i=1; i<arguments.length; i++)
|
||||||
|
if(arguments[i] != null)
|
||||||
|
arguments[0].appendChild(tag(arguments[i]));
|
||||||
|
}
|
||||||
function setText(obj, txt) {
|
function setText(obj, txt) {
|
||||||
if(obj.textContent != null)
|
if(obj.textContent != null)
|
||||||
obj.textContent = txt;
|
obj.textContent = txt;
|
||||||
|
|
@ -85,9 +120,196 @@ function setClass(obj, c, set) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Dropdown Search
|
||||||
|
|
||||||
|
function dsInit(obj, url, trfunc, serfunc, retfunc, parfunc) {
|
||||||
|
obj.setAttribute('autocomplete', 'off');
|
||||||
|
obj.onkeydown = dsKeyDown;
|
||||||
|
obj.onblur = function() { setTimeout(function () { setClass(byId('ds_box'), 'hidden', true) }, 250) };
|
||||||
|
obj.ds_returnFunc = retfunc;
|
||||||
|
obj.ds_trFunc = trfunc;
|
||||||
|
obj.ds_serFunc = serfunc;
|
||||||
|
obj.ds_parFunc = parfunc;
|
||||||
|
obj.ds_searchURL = url;
|
||||||
|
obj.ds_selectedId = 0;
|
||||||
|
obj.ds_dosearch = null;
|
||||||
|
if(!byId('ds_box'))
|
||||||
|
addBody(tag('div', {id: 'ds_box', 'class':'hidden'}, tag('b', 'Loading...')));
|
||||||
|
}
|
||||||
|
|
||||||
|
function dsKeyDown(ev) {
|
||||||
|
var c = document.layers ? ev.which : document.all ? event.keyCode : ev.keyCode;
|
||||||
|
var obj = this;
|
||||||
|
|
||||||
|
if(c == 9) // tab
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// do some processing when the enter key has been pressed
|
||||||
|
if(c == 13) {
|
||||||
|
var frm = obj;
|
||||||
|
while(frm && frm.nodeName.toLowerCase() != 'form')
|
||||||
|
frm = frm.parentNode;
|
||||||
|
if(frm) {
|
||||||
|
var oldsubmit = frm.onsubmit;
|
||||||
|
frm.onsubmit = function() { return false };
|
||||||
|
setTimeout(function() { frm.onsubmit = oldsubmit }, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(obj.ds_selectedId != 0)
|
||||||
|
obj.value = obj.ds_serFunc(byId('ds_box_'+obj.ds_selectedId).ds_itemData, obj);
|
||||||
|
if(obj.ds_returnFunc)
|
||||||
|
obj.ds_returnFunc(obj);
|
||||||
|
|
||||||
|
setClass(byId('ds_box'), 'hidden', true);
|
||||||
|
setContent(byId('ds_box'), tag('b', 'Loading...'));
|
||||||
|
obj.ds_selectedId = 0;
|
||||||
|
if(obj.ds_dosearch) {
|
||||||
|
clearTimeout(obj.ds_dosearch);
|
||||||
|
obj.ds_dosearch = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process up/down keys
|
||||||
|
if(c == 38 || c == 40) {
|
||||||
|
var l = byName(byId('ds_box'), 'tr');
|
||||||
|
if(l.length < 1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// get new selected id
|
||||||
|
if(obj.ds_selectedId == 0) {
|
||||||
|
if(c == 38) // up
|
||||||
|
obj.ds_selectedId = l[l.length-1].id.substr(7);
|
||||||
|
else
|
||||||
|
obj.ds_selectedId = l[0].id.substr(7);
|
||||||
|
} else {
|
||||||
|
var sel = null;
|
||||||
|
for(var i=0; i<l.length; i++)
|
||||||
|
if(l[i].id == 'ds_box_'+obj.ds_selectedId) {
|
||||||
|
if(c == 38) // up
|
||||||
|
sel = i>0 ? l[i-1] : l[l.length-1];
|
||||||
|
else
|
||||||
|
sel = l[i+1] ? l[i+1] : l[0];
|
||||||
|
}
|
||||||
|
obj.ds_selectedId = sel.id.substr(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set selected class
|
||||||
|
for(var i=0; i<l.length; i++)
|
||||||
|
setClass(l[i], 'selected', l[i].id == 'ds_box_'+obj.ds_selectedId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform search after a timeout
|
||||||
|
if(obj.ds_dosearch)
|
||||||
|
clearTimeout(obj.ds_dosearch);
|
||||||
|
obj.ds_dosearch = setTimeout(function() {
|
||||||
|
dsSearch(obj);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dsSearch(obj) {
|
||||||
|
var box = byId('ds_box');
|
||||||
|
var val = obj.ds_parFunc ? obj.ds_parFunc(obj.value) : obj.value;
|
||||||
|
|
||||||
|
clearTimeout(obj.ds_dosearch);
|
||||||
|
obj.ds_dosearch = null;
|
||||||
|
|
||||||
|
// hide the ds_box div
|
||||||
|
if(val.length < 2) {
|
||||||
|
setClass(box, 'hidden', true);
|
||||||
|
setContent(box, tag('b', 'Loading...'));
|
||||||
|
obj.ds_selectedId = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// position the div
|
||||||
|
var ddx=0;
|
||||||
|
var ddy=obj.offsetHeight;
|
||||||
|
var o = obj;
|
||||||
|
do {
|
||||||
|
ddx += o.offsetLeft;
|
||||||
|
ddy += o.offsetTop;
|
||||||
|
} while(o = o.offsetParent);
|
||||||
|
|
||||||
|
box.style.position = 'absolute';
|
||||||
|
box.style.left = ddx+'px';
|
||||||
|
box.style.top = ddy+'px';
|
||||||
|
box.style.width = obj.offsetWidth+'px';
|
||||||
|
setClass(box, 'hidden', false);
|
||||||
|
|
||||||
|
// perform search
|
||||||
|
ajax(obj.ds_searchURL + encodeURIComponent(val), function(hr) {
|
||||||
|
dsResults(hr, obj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dsResults(hr, obj) {
|
||||||
|
var lst = hr.responseXML.getElementsByTagName('item');
|
||||||
|
var box = byId('ds_box');
|
||||||
|
if(lst.length < 1) {
|
||||||
|
setContent(box, tag('b', 'No results'));
|
||||||
|
obj.selectedId = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tb = tag('tbody', null);
|
||||||
|
for(var i=0; i<lst.length; i++) {
|
||||||
|
var id = lst[i].getAttribute('id');
|
||||||
|
var tr = tag('tr', {id: 'ds_box_'+id, ds_itemData: lst[i]} );
|
||||||
|
setClass(tr, 'selected', obj.selectedId == id);
|
||||||
|
|
||||||
|
tr.onmouseover = function() {
|
||||||
|
obj.ds_selectedId = this.id.substr(7);
|
||||||
|
var l = byName(box, 'tr');
|
||||||
|
for(var j=0; j<l.length; j++)
|
||||||
|
setClass(l[j], 'selected', l[j].id == 'ds_box_'+obj.ds_selectedId);
|
||||||
|
};
|
||||||
|
tr.onmousedown = function() {
|
||||||
|
obj.value = obj.ds_serFunc(this.ds_itemData, obj);
|
||||||
|
if(obj.ds_returnFunc)
|
||||||
|
obj.ds_returnFunc();
|
||||||
|
setClass(box, 'hidden', true);
|
||||||
|
obj.ds_selectedId = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
obj.ds_trFunc(lst[i], tr);
|
||||||
|
tb.appendChild(tr);
|
||||||
|
}
|
||||||
|
setContent(box, tag('table', tb));
|
||||||
|
|
||||||
|
if(obj.ds_selectedId != 0 && !byId('ds_box_'+obj.ds_selectedId))
|
||||||
|
obj.ds_selectedId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* What follows is specific to manned.org */
|
/* What follows is specific to manned.org */
|
||||||
|
|
||||||
|
// Search box
|
||||||
|
searchRedir = false;
|
||||||
|
dsInit(byId('q'), '/xml/search.xml?q=', function(item, tr) {
|
||||||
|
tr.appendChild(tag('td', item.getAttribute('name'), tag('i', '('+item.getAttribute('section')+')')));
|
||||||
|
},
|
||||||
|
function(item) {
|
||||||
|
searchRedir = true;
|
||||||
|
location.href = '/'+item.getAttribute('name')+'.'+item.getAttribute('section').substr(0,1);
|
||||||
|
return item.getAttribute('name')+'('+item.getAttribute('section')+')';
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
if(!searchRedir) {
|
||||||
|
var frm=byId('q');
|
||||||
|
while(frm && frm.nodeName.toLowerCase() != 'form')
|
||||||
|
frm = frm.parentNode;
|
||||||
|
frm.submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
// TODO: Fix the 'pkg' link
|
// TODO: Fix the 'pkg' link
|
||||||
// TODO: Keep same view when switching to different version of the same man page
|
// TODO: Keep same view when switching to different version of the same man page
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue