diff --git a/www/index.pl b/www/index.pl
index d3b6e03..168ac61 100755
--- a/www/index.pl
+++ b/www/index.pl
@@ -7,6 +7,7 @@ use IPC::Open2;
use IO::Select;
use Encode 'encode_utf8', 'decode_utf8';
use Time::HiRes 'tv_interval', 'gettimeofday';
+use JSON::XS;
use Cwd 'abs_path';
our $ROOT;
@@ -243,44 +244,6 @@ sub browsepkg {
}
-sub manselect {
- my($self, $lst, $selhash) = @_;
- return if !@$lst;
-
- $selhash ||= '';
-
- my %sys;
- push @{$sys{$_->{system}}}, $_ for (@$lst);
-
- dl id => 'nav';
- my $lastname = '';
- for my $sys (sort { my $x=$self->{sysbyid}{$a}; my $y=$self->{sysbyid}{$b}; $x->{name} cmp $y->{name} or $y->{relorder} <=> $x->{relorder} } keys %sys) {
- my %pkgs;
- push @{$pkgs{"$_->{package}-$_->{version}"}}, $_ for @{$sys{$sys}};
- dt $lastname eq $self->{sysbyid}{$sys}{name} ? (class => 'oldrelease') : (), $self->{sysbyid}{$sys}{full};
- dd;
- for my $pkg (sort { $pkgs{$a}[0]{package} cmp $pkgs{$b}[0]{package} || $pkgs{$b}[0]{released} cmp $pkgs{$a}[0]{released} } keys %pkgs) {
- dl;
- dt;
- txt $pkgs{$pkg}[0]{package};
- i $pkgs{$pkg}[0]{version};
- end;
- dd;
- for my $man (sort { $a->{section} cmp $b->{section} || ($a->{locale}||'') cmp ($b->{locale}||'') } @{$pkgs{$pkg}}) {
- my $t = $man->{locale} ? "$man->{section}.$man->{locale}" : $man->{section};
- a href => sprintf('/%s/%s', $man->{name}, substr $man->{hash}, 0, 8), $t if $selhash ne $man->{hash};
- b $t if $selhash eq $man->{hash};
- }
- end;
- end;
- }
- end 'dd';
- $lastname = $self->{sysbyid}{$sys}{name};
- }
- end 'dl';
-}
-
-
# TODO: Store/cache the result of this of this function in the database.
sub manfmt {
my $c = shift;
@@ -332,10 +295,48 @@ sub manfmt {
waitpid $pid, 0;
$ret = decode_utf8($ret);
+ $ret =~ s/[\t\s\r\n]+$//;
return $ret;
}
+sub manjslist {
+ my($self, $m) = @_;
+
+ # For JS: (Already sorted)
+ # [
+ # ["System", "Full name", [
+ # [ "package", "version", [
+ # [ "section", "locale"||null ],
+ # ...
+ # ],
+ # ],
+ # ...
+ # ],
+ # ],
+ # ...
+ # ]
+ my %sys;
+ push @{$sys{$_->{system}}}, $_ for (@$m);
+ [
+ map [ $self->{sysbyid}{$_}{name}, $self->{sysbyid}{$_}{full},
+ do {
+ my %pkgs;
+ for(@{$sys{$_}}) {
+ my $pn = "$_->{package}-$_->{version}";
+ $pkgs{$pn} = [ $_->{package}, $_->{version}, [], $_->{released} ] if !$pkgs{$pn};
+ push @{$pkgs{$pn}[2]}, [ $_->{section}, $_->{locale}, substr $_->{hash}, 0, 8 ];
+ }
+ [ grep
+ delete($_->[3]) && ($_->[2] = [sort { $a->[0] cmp $b->[0] || ($a->[1]||'') cmp ($b->[1]||'') } @{$_->[2]}]),
+ sort { $a->[0] cmp $b->[0] || $b->[3] cmp $a->[3] } values %pkgs ];
+ }
+ ],
+ sort { my $x=$self->{sysbyid}{$a}; my $y=$self->{sysbyid}{$b}; $x->{name} cmp $y->{name} or $y->{relorder} <=> $x->{relorder} } keys %sys
+ ]
+}
+
+
# Given the name and optionally the hash of a man page, check with a list of
# man pages with the same name to select the right one for display.
sub getman {
@@ -348,8 +349,9 @@ sub getman {
$_->{hash} =~ /^$hash/ && return $_ for (@$list);
}
- # If that failed, sort the list based on some heuristics.
- my @l = sort {
+ # If that failed, use some heuristics
+ my $cmp = sub {
+ local($a,$b) = @_;
# English or non-locale packages always win
!(($a->{locale}||'') =~ /^(en|$)/) != !(($b->{locale}||'') =~ /^(en|$)/)
? (($a->{locale}||'') =~ /^(en|$)/ ? -1 : 1)
@@ -376,9 +378,11 @@ sub getman {
? $a->{section} cmp $b->{section}
# Fallback to hash if nothing else matters (guarantees the order is at least stable)
: $a->{hash} cmp $b->{hash};
- } @$list;
+ };
- return $l[0];
+ my $winner = $list->[0];
+ $cmp->($winner, $_) > 0 and ($winner = $_) for (@$list);
+ return $winner;
}
@@ -390,7 +394,7 @@ sub man {
my $man = getman($self, $name, $hash, $m);
$self->htmlHeader(title => $name);
- manselect $self, $m, $man->{hash};
+ dl id => 'nav', ' '; # To be filled in by JS
h1 $man->{name};
p;
@@ -401,8 +405,7 @@ sub man {
div id => 'contents';
my $c = $self->dbManContent($man->{hash});
- ($c = GrottyParser::html(manfmt $c)) =~ s/[\t\s\r\n]+$//; # TODO: <- Do this in GrottyParser
- pre; lit $c; end;
+ pre; lit GrottyParser::html(manfmt $c); end;
end;
div id => 'locations';
@@ -437,7 +440,7 @@ sub man {
end;
end;
- $self->htmlFooter;
+ $self->htmlFooter(js => { hash => substr($man->{hash}, 0, 8), name => $man->{name}, mans => manjslist($self, $m) });
}
@@ -465,11 +468,6 @@ sub htmlHeader {
html;
head;
Link rel => 'stylesheet', type => 'text/css', href => '/man.css';
- style type => 'text/css';
- lit 'thead tr { font-weight: bold; border-bottom: 1px solid #ccc }';
- lit 'table td { border-left: 1px solid #ccc; padding: 0 3px }';
- lit 'table { border-collapse: collapse }';
- end;
title $o{title}.' - manned.org';
end 'head';
body;
@@ -487,7 +485,7 @@ sub htmlHeader {
sub htmlFooter {
- my $self = shift;
+ my($self, %o) = @_;
br style => 'clear: both';
end;
@@ -495,6 +493,14 @@ sub htmlFooter {
lit 'All manual pages are copyrighted by their respective authors.
| About manned.org | Contact';
end;
+ if($o{js}) {
+ script type => 'text/javascript';
+ lit 'VARS = ';
+ lit(JSON::XS->new->ascii->encode($o{js}));
+ lit ';';
+ end;
+ }
+ script type => 'text/javascript', src => '/man.js', '';
end;
end 'html';
diff --git a/www/man.css b/www/man.css
index f48ef8f..949c9f1 100644
--- a/www/man.css
+++ b/www/man.css
@@ -16,8 +16,9 @@ dd { margin-left: 15px; }
a { color: #048; font-family: "Verdana"; font-weight: normal; text-decoration: underline; padding: 3px 5px;
-webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; }
a:hover { text-decoration: none; background: #cde; }
-table { background: #eee; border: 5px solid #f8f8f8; margin: 10px 0; }
-td { padding: 1px 5px; font-size: 12px; }
+table { background: #eee; border: 5px solid #f8f8f8; margin: 10px 0; border-collapse: collapse; }
+thead tr { font-weight: bold; border-bottom: 1px solid #ccc }
+td { padding: 1px 5px; font-size: 12px; border-left: 1px solid #ccc; }
#header { padding: 4px 20px; border-bottom: 1px solid #888; font: 24px "Arial";
-webkit-border-radius: 8px 8px 0 0; -moz-border-radius: 8px 8px 0 0; border-radius: 8px 8px 0 0;
diff --git a/www/man.js b/www/man.js
new file mode 100644
index 0000000..e528555
--- /dev/null
+++ b/www/man.js
@@ -0,0 +1,61 @@
+/* The following functions are part of a minimal JS library I wrote for VNDB.org */
+
+function byId(n) {
+ return document.getElementById(n)
+}
+
+/* wrapper around DOM element creation
+ * tag('string') -> createTextNode
+ * tag('tagname', tag(), 'string', ..) -> createElement(), appendChild(), ..
+ * tag('tagname', { class: 'meh', title: 'Title' }) -> createElement(), setAttribute()..
+ * tag('tagname', { }, ) -> create, setattr, append */
+function tag() {
+ if(arguments.length == 1)
+ return typeof arguments[0] != 'object' ? document.createTextNode(arguments[0]) : arguments[0];
+ var el = typeof document.createElementNS != 'undefined'
+ ? document.createElementNS('http://www.w3.org/1999/xhtml', arguments[0])
+ : document.createElement(arguments[0]);
+ for(var i=1; i 0)
+ pdd.appendChild(tag(' '));
+ pdd.appendChild(man[2] == VARS.hash ? tag('b', txt) : tag('a', {href:'/'+VARS.name+'/'+man[2]}, txt));
+ }
+ dd.appendChild(pdt);
+ dd.appendChild(pdd);
+ }
+ nav.appendChild(dt);
+ nav.appendChild(dd);
+ }
+}
+