diff --git a/www/index.pl b/www/index.pl
index 84a4dbb..70e488c 100755
--- a/www/index.pl
+++ b/www/index.pl
@@ -67,10 +67,21 @@ TUWF::register(
$self->resRedirect("/pkg/$sys->{short}/$pkgs->[0]{category}/$name".($ver ? "/$ver" :''), 'perm');
},
- qr{xml/search\.xml} => \&xmlsearch,
qr{([^/]+)/([0-9a-f]{8})} => \&man,
qr{([^/]+)/([0-9a-f]{8})/src} => \&src,
qr{([^/]+)} => \&man,
+
+ # Redirect for a specific language for a man page.
+ # I'm not a fan of this solution; might drop it in the future.
+ qr{lang/([^/]+)/([^/]+)} => sub {
+ my($s, $l, $n) = @_;
+ my($m, undef) = $s->dbManPrefName($n, language => $l);
+ return $s->resNotFound if !$m;
+ $s->resRedirect("/$m->{name}/".substr($m->{hash}, 0, 8), 'temp');
+ },
+
+ qr{xml/search\.xml} => \&xmlsearch,
+ qr{json/tree\.json} => \&jsontree,
);
TUWF::run();
@@ -441,7 +452,7 @@ sub pkg_info {
my $f = $self->formValidate({ get => 's', required => 0});
return $self->resNotFound if $f->{_err};
- my $mans = $self->dbManInfo(package => $sel->{id}, results => 201, start => $f->{s}, sort => 'name');
+ my $mans = $self->dbManInfo(package => $sel->{id}, results => 201, start => $f->{s}, sort => 'syspkgname');
my $more = @$mans > 200 && pop @$mans;
# Latest version of this package determines last modification date of the page.
@@ -520,28 +531,42 @@ sub man_redir {
};
-sub manjslist {
- my($self, $m) = @_;
+sub _man_langsect {
+ my($self, $man) = @_;
- # The structure we generate is described in the JS code.
- my %sys;
- push @{$sys{$_->{system}}}, $_ for (@$m);
- [
- map [ $self->{sysbyid}{$_}{name}, $self->{sysbyid}{$_}{full}, $self->{sysbyid}{$_}{short},
- do {
- my %pkgs;
- for(@{$sys{$_}}) {
- my $pn = "$_->{category}-$_->{package}-$_->{version}";
- $pkgs{$pn} = [ $_->{category}, $_->{package}, $_->{version}, [], $_->{released} ] if !$pkgs{$pn};
- push @{$pkgs{$pn}[3]}, [ $_->{section}, $_->{locale}, substr $_->{hash}, 0, 8 ];
- }
- [ grep
- delete($_->[4]) && ($_->[3] = [sort { $a->[0] cmp $b->[0] || ($a->[1]||'') cmp ($b->[1]||'') } @{$_->[3]}]),
- sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] || $b->[4] cmp $a->[4] } 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
- ]
+ # TODO: This is ugly, especially because clicking on a translation or
+ # section, you can end up with a man page that is nowhere close to the man
+ # page you're currently reading. Opening a version selector box might be a
+ # better alternative.
+
+ my @sect = $self->dbManSections($man->{name});
+ if(@sect > 1) {
+ div id => 'sectionselect', class => 'hidden';
+ for (@sect) {
+ if($man->{section} eq $_) {
+ i $_;
+ } else {
+ a href => "/$man->{name}.$_", $_;
+ }
+ txt ' ';
+ }
+ end;
+ }
+
+ my @lang = $self->dbManLanguages($man->{name}, $man->{section});
+ if(@lang > 1) {
+ div id => 'langselect', class => 'hidden';
+ (my $cur = $man->{locale}||'') =~ s/\..*//;
+ for (@lang) {
+ if(($_||'') eq $cur) {
+ i $_ || 'default';
+ } else {
+ a href => $_ ? "/lang/$_/$man->{name}.$man->{section}" : "/$man->{name}.$man->{section}", $_ || 'default';
+ }
+ txt ' ';
+ }
+ end;
+ }
}
@@ -560,63 +585,25 @@ sub man {
}
return $self->resNotFound() if !$man;
- my $view = $self->formValidate({get => 'v', regex => qr/^[a-z2-7]+$/});
- $view = $view->{_err} ? '' : $view->{v};
-
- # To be really correct, the last modification time of this page should be the
- # release date of the latest version of the man page, as that is displayed in
- # the menu. But let's just consider the content of the page as more
- # important, and use release date of the man page as last modification date.
$self->setLastMod($man->{released});
-
$self->htmlHeader(title => $name);
- div id => 'nav', 'Sorry, this navigation menu won\'t display without Javascript. :-(';
-
- h1;
- txt $man->{name}.' ';
- a href => "/$man->{name}/".substr($man->{hash}, 0, 8).'/src', 'source';
+ div id => 'manbuttons';
+ h1 $man->{name};
+ ul 'data-hash' => $man->{hash}, 'data-name' => $man->{name}, 'data-section' => $man->{section}, 'data-locale' => $man->{locale}||'',
+ 'data-hasversions' => $self->dbManHasVersions($man->{name}, $man->{section}, $man->{locale}, $man->{hash});
+ li; a href => "/$man->{name}/".substr($man->{hash}, 0, 8).'/src', 'source'; end;
+ li; a href => "/$man->{name}/".substr($man->{hash}, 0, 8), 'permalink'; end;
+ end;
+ end;
+ div id => 'manres', class => 'hidden';
+ _man_langsect($self, $man);
end;
div id => 'contents';
my $c = $self->dbManContent($man->{hash});
- # TODO: Store/cache the result of fmt() in the database.
pre; lit ManUtils::html(ManUtils::fmt_block $c); end;
end;
-
- div id => 'locations';
- h2 'Locations of this man page';
- table;
- thead; Tr;
- td 'System';
- td 'Package';
- td 'Version';
- td 'Name';
- td 'Filename';
- end; end;
- my @l = sort {
- $self->{sysbyid}{$a->{system}}{name} cmp $self->{sysbyid}{$b->{system}}{name}
- || $self->{sysbyid}{$b->{system}}{relorder} <=> $self->{sysbyid}{$a->{system}}{relorder}
- || $b->{released} cmp $a->{released}
- || $a->{filename} cmp $b->{filename}
- } @{$self->dbManInfo(hash => $man->{hash})};
- for(@l) {
- Tr;
- td $self->{sysbyid}{$_->{system}}{full};
- td "$_->{category}/$_->{package}";
- td $_->{version};
- td;
- a href => "/$_->{name}", $_->{name} if $_->{name} ne $man->{name};
- txt $_->{name} if $_->{name} eq $man->{name};
- txt ".$_->{section}";
- end;
- td $_->{filename};
- end;
- }
- end;
- end;
-
- my $m = $self->dbManInfo(name => $man->{name});
- $self->htmlFooter(js => { hash => substr($man->{hash}, 0, 8), name => $man->{name}, view => $view, mans => manjslist($self, $m) });
+ $self->htmlFooter();
}
@@ -648,6 +635,93 @@ sub xmlsearch {
}
+sub jsontree {
+ my $self = shift;
+
+ my $f = $self->formValidate(
+ { get => 'name', required => 0, maxlength => 256 },
+ { get => 'section', required => 0, maxlength => 32 },
+ { get => 'locale', required => 0, default => '', maxlength => 32 },
+ { get => 'cur', required => 0, default => '', regex => qr/^[a-fA-F0-9]{40}$/ },
+ { get => 'hash', required => 0, default => '', regex => qr/^[a-fA-F0-9]{40}$/ },
+ );
+ return $self->resNotFound() if $f->{_err} || (!$f->{hash} && !($f->{section} && $f->{name}));
+
+ my $l = $self->dbManInfo(sort => 'syspkgname', $f->{hash}
+ ? (hash => $f->{hash})
+ : (name => $f->{name}, section => $f->{section}, locale => $f->{locale}));
+
+ # Convert the list into a tree
+ my $tree = [];
+ my($sys, $sysver, $pkg, $pkgver);
+ for my $m (@$l) {
+ my $sysname = $self->{sysbyid}{$m->{system}}{name};
+ if(!$sys || $sysname ne $sys->{name}) {
+ $sys = { name => $sysname, childs => [] };
+ $sysver = undef;
+ push @$tree, $sys;
+ }
+
+ my $sysversion = $self->{sysbyid}{$m->{system}}{release} || '';
+ if(!$sysver || $sysversion ne $sysver->{name}) {
+ $sysver = { name => $sysversion, childs => [] };
+ $pkg = undef;
+ push @{$sys->{childs}}, $sysver;
+ }
+
+ if(!$pkg || $m->{package} ne $pkg->{name}) {
+ $pkg = { name => $m->{package}, i => $m->{category}, table => [] };
+ $pkgver = undef;
+ push @{$sysver->{childs}}, $pkg;
+ }
+
+ push @{$pkg->{table}}, [
+ $pkgver && $pkgver eq $m->{version} ? {name=>''} :
+ {name => $m->{version}, href => "/pkg/$self->{sysbyid}{$m->{system}}{short}/$m->{category}/$m->{package}/$m->{version}"},
+ { name => "$m->{name}($m->{section})",
+ $f->{hash} || lc($m->{hash}) eq lc($f->{cur}) ? ()
+ : (href => sprintf('/%s/%s', $m->{name}, substr $m->{hash}, 0, 8))
+ },
+ { name => substr($m->{hash}, 0, 8),
+ $f->{hash} || lc($m->{hash}) eq lc($f->{cur}) ? ()
+ : (href => sprintf('/%s/%s', $m->{name}, substr $m->{hash}, 0, 8))
+ },
+ { name => $m->{filename} }
+ ];
+ $pkgver = $m->{version};
+ }
+
+ # Determine which elements to show/hide by default.
+ # It might make more sense to do this in JS, but since I am utterly
+ # incapable of writing maintainable JS I'm doing it here in order to keep the
+ # JS stupid and simple.
+ # TODO: Highlight systems/packages where the 'current' man page is?
+ for my $sys (@$tree) {
+ $sys->{expand} = 1 if $sys->{childs}[0]{name}; # Expand all systems that have named versions
+ $sys->{expand} = 1 if $f->{hash}; # Expand everything on 'location'
+
+ my $i = 0;
+ for my $sysver (@{$sys->{childs}}) {
+ $i++;
+ $sysver->{expand} = 1 if !$sysver->{name}; # Expand unnamed versions (since you can't click them)
+ $sysver->{expand} = 1 if $f->{hash}; # Expand everything on 'location'
+ $sysver->{hide} = 1 if $i > 3 && @{$sys->{childs}} > 5; # Show only the first 3 versions
+
+ for my $pkg (@{$sysver->{childs}}) {
+ $pkg->{expand} = 1 if @{$sysver->{childs}} <= 3; # Expand everything if there's not too many things to expand
+ $pkg->{expand} = 1 if $f->{hash}; # Expand everything on 'location'
+
+ # TODO: Show/Hide duplicate hashes?
+ }
+ }
+ }
+
+ # Why JSON? Because TUWF::XML is pretty slow with many nodes
+ $self->resHeader('Content-Type' => 'application/json; charset=UTF-8');
+ lit(JSON::XS->new->ascii->encode($tree));
+}
+
+
package TUWF::Object;
@@ -694,13 +768,6 @@ sub htmlFooter {
| Contact
| Source';
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';
@@ -746,26 +813,28 @@ sub dbManInfo {
my %where = (
$o{name} ? ('m.name = ?' => $o{name}) : (),
$o{package} ? ('m.package = ?' => $o{package}) : (),
- $o{section} ? ('m.section = ?' => $o{section}) : (),
+ defined($o{section}) ? ('m.section = ?' => $o{section}) : (),
+ $o{locale} ? ('m.locale = ?' => $o{locale}) : (),
+ defined($o{locale}) && !$o{locale} ? ('m.locale IS NULL' => 1) : (),
$o{shorthash} ? (q{substring(m.hash from 1 for 4) = decode(?, 'hex')} => $o{shorthash}) : (),
$o{hash} ? (q{m.hash = decode(?, 'hex')} => $o{hash}) : (),
$o{start} ? ('m.name > ?' => $o{start}) : (),
);
- # TODO: Flags to indicate what to information to fetch
+ $o{sort} ||= '';
+ my $order =
+ $o{sort} eq 'syspkgname' ? 'ORDER BY s.name, s.relorder DESC, p.name, v.released DESC, m.name, m.locale NULLS FIRST, m.filename' : '';
+
return $s->dbAll(q{
- SELECT p.system, p.category, p.name AS package, pv.version, pv.released, m.name, m.section, m.filename, m.locale, encode(m.hash, 'hex') AS hash
- FROM packages p
- JOIN package_versions pv ON p.id = pv.package
- JOIN man m ON m.package = pv.id
+ SELECT p.system, p.category, p.name AS package, v.version, v.released, m.name, m.section, m.filename, m.locale, encode(m.hash, 'hex') AS hash
+ FROM man m
+ JOIN package_versions v ON v.id = m.package
+ JOIN packages p ON p.id = v.package
+ JOIN systems s ON s.id = p.system
!W
!s
LIMIT ?
- },
- \%where,
- $o{sort} ? 'ORDER BY name' : '',
- $o{results}||10000
- );
+ }, \%where, $order, $o{results}||10000);
}
@@ -796,6 +865,7 @@ sub dbManPref {
$o{sysid} ? ('p.system = ?' => $o{sysid}) : (),
$o{package} ? ('p.id = ?' => $o{package}) : (),
$o{pkgver} ? ('v.id = ?' => $o{pkgver}) : (),
+ $o{language}? (q{substring(locale from '^[^.]+') = ?} => $o{language}) : (),
);
# Criteria to determine a "preferred" man page:
@@ -854,6 +924,33 @@ sub dbManPrefName {
}
+# Returns 1 of there are alternative versions of the given man page.
+sub dbManHasVersions {
+ my($s, $name, $section, $locale, $hash) = @_;
+ return $s->dbRow(
+ q{SELECT 1 AS ok FROM man WHERE name = ? AND section = ? AND locale IS NOT DISTINCT FROM ? AND hash <> decode(?, 'hex') LIMIT 1},
+ $name, $section, $locale, $hash
+ )->{ok}||0;
+}
+
+
+# Returns all available languages for a man page
+sub dbManLanguages {
+ my($s, $name, $section) = @_;
+ return map $_->{lang}, @{$s->dbAll(q{SELECT DISTINCT substring(locale from '^[^.]+') AS lang
+ FROM man WHERE name = ? AND section = ?
+ ORDER BY substring(locale from '^[^.]+') NULLS FIRST
+ }, $name, $section)};
+}
+
+
+# Returns all available languages for a man page
+sub dbManSections {
+ my($s, $name) = @_;
+ return map $_->{section}, @{$s->dbAll(q{SELECT DISTINCT section FROM man WHERE name = ? ORDER BY section}, $name)};
+}
+
+
sub dbSystemGet {
return shift->dbAll('SELECT id, name, release, short, relorder FROM systems ORDER BY name, relorder');
}
diff --git a/www/man.css b/www/man.css
index c4efe8b..9c62c52 100644
--- a/www/man.css
+++ b/www/man.css
@@ -2,55 +2,33 @@
* { margin: 0; padding: 0; font-family: "Trebuchet MS", sans-serif; }
html { background: #333; padding: 0 10px; }
-body { margin: 10px auto 50px auto; max-width: 1250px; border-collapse: separate; padding-bottom: 10px;
- -webkit-border-radius: 10px; -moz-border-radius: 10px;
- -webkit-box-shadow: 0 10px 10px #def; -moz-box-shadow: 0 10px 10px #def; box-shadow: 0 10px 10px #def; }
+body { margin: 10px auto 50px auto; max-width: 1250px; border-collapse: separate; padding-bottom: 10px; border-radius: 10px; box-shadow: 0 10px 10px #def; }
h1 { font-size: 24px; font-weight: normal; color: #abc; }
-h1 a { font-size: 12px; vertical-align: top }
h2 { font-size: 21px; margin-top: 40px; color: #468; font-weight: normal; clear: left }
h2 + i { font-size: 12px; }
h3 { font-size: 16px; margin-top: 20px; color: #468; font-weight: normal }
dd { margin-left: 15px; }
a { color: #048; }
a:hover { text-decoration: underline; color: #48B;}
-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; }
code { font-family: "Lucida Console", Monospace; font-size: 12px; background-color: #f0f8ff }
+.hidden { display: none!important; }
-#header { padding: 4px 20px; border-bottom: 1px solid #888; font: 24px "Arial"; background: url('images/gradients.png') repeat-x;
- -webkit-border-radius: 8px 8px 0 0; -moz-border-radius: 8px 8px 0 0; border-radius: 8px 8px 0 0; }
+#header { padding: 4px 20px; border-bottom: 1px solid #888; font: 24px "Arial"; background: url('images/gradients.png') repeat-x; border-radius: 8px 8px 0 0; }
#header a { color: #f8f8f8; text-decoration: none; font-weight: bold; }
#header a:hover { background: none; }
#header form { float: right; }
#header input[type=text] { color: #000; width: 260px; padding: 1px 2px 1px 15px; border-radius: 12px 0 0 12px; border: 1px solid #444;
- -webkit-box-shadow: 1px 1px 3px #fff, -1px -1px 2px #234; -moz-shadow: 1px 1px 3px #fff, -1px -1px 2px #234; box-shadow: 1px 1px 3px #fff, -1px -1px 2px #234;
- background: url('images/gradients.png') 0 -105px repeat-x; height: 15px; }
+ box-shadow: 1px 1px 3px #fff, -1px -1px 2px #234; background: url('images/gradients.png') 0 -105px repeat-x; height: 15px; }
#header input[type=text]:hover, #header input[type=text]:focus { background: url('images/gradients.png') 0 -122px repeat-x; outline: none; }
#header input[type=submit] { height: 23px; width: 62px; background-image: url('images/search.png'); border: 0; margin: 0 0 0 -5px; cursor: pointer }
#body { padding: 10px 10px 20px 10px; background: #fff }
#systems a,
-#charselect a,
-#nav dd dd a { color: #048; font-family: "Verdana"; font-weight: normal; text-decoration: none; padding: 3px 5px;
- -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; }
-#systems a:hover,
-#charselect a:hover,
-#nav dd dd a:hover { background: #cde; }
+#charselect a { color: #048; font-family: "Verdana"; font-weight: normal; text-decoration: none; padding: 3px 5px; border-radius: 4px; }
-#nav { background: #f0f8ff; color: #036; float: right; padding: 8px; width: 250px; margin-bottom: 10px;
- -webkit-border-radius: 8px; -moz-border-radius: 8px; border-radius: 8px; }
-#nav a.global, #nav i.global { float: right; font-family: "Verdana"; font-size: 13px; padding: 0px 5px; }
-#nav i.global { font-style: normal; color: #aaa }
-#nav dl > dt { font-weight: bold; }
-#nav a { font-size: 13px; }
-#nav dd dt a { text-decoration: none; }
-#nav .expand { text-decoration: none; padding-left: 5px; font-size: 16px }
-#nav dd .expand { font-size: 14px }
-#nav dl i { font-style: normal; font-size: 12px; padding-left: 7px; color: #aaa }
-#nav b { font-size: 13px; background: #cde; padding: 3px 5px;
- -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; }
+#systems a:hover,
+#charselect a:hover { background: #cde; }
i.grayedout { color: #aaa; font-size: 13px; }
@@ -63,7 +41,6 @@ i.grayedout { color: #aaa; font-size: 13px; }
#systems li { display: block; float: left; width: 300px; min-height: 70px; margin: 20px 10px; padding-left: 60px }
#systems span { display: block; margin-left: -55px; float: left; width: 50px; height: 50px; background-repeat: no-repeat; }
#systems b { font-size: 24px; display: block }
-#systems .hidden { display: none; }
#charselect { float: right }
#charselect b { padding: 3px }
@@ -96,9 +73,39 @@ i.grayedout { color: #aaa; font-size: 13px; }
#pkgmans li { display: block; }
#pkgmans i { color: #aaa; font-size: 13px; }
+#manbuttons h1 { display: inline; margin: 0 20px 0 0; vertical-align: middle }
+#manbuttons ul { list-style-type: none; display: inline-block }
+#manbuttons li { display: inline-block }
+#manbuttons li a, #manbuttons li i { display: inline-block; outline: none; margin: 0 10px 0 0; padding: 5px 7px 8px 7px; text-decoration: none; border-top-left-radius: 7px 5px; border-top-left-radius: 7px 5px }
+#manbuttons li i { font-style: normal; text-decoration: line-through; color: #aaa }
+#manbuttons li a:hover, #manbuttons li a.active { background: #f0f8ff }
+
+#closebtn { float: right; margin: -5px 0 -20px 0; text-decoration: none; outline: none; color: #036; font-weight: bold }
+
+#manres { margin: 0 0 10px 0; width: 100%; padding: 10px; box-sizing: border-box; background: #f0f8ff; border-radius: 10px; border-left: 1px dashed #333; border-right: 1px dashed #333 }
+#manres i { color: #aaa; font-size: 13px; margin-left: 7px }
+#manres ul { list-style-type: none }
+#manres ul a { outline: none; text-decoration: none }
+#manres ul .oldver a { color: #aaa; font-size: 13px }
+#manres div > ul { margin-top: 5px } /* System names */
+#manres div > ul > li > a { color: #036; font-weight: bold } /* System names */
+#manres div > ul > li > ul { margin-left: 15px } /* System versions */
+#manres div > ul > li > ul > li > a { color: #000 } /* System versions */
+#manres div > ul > li > ul > li > ul { margin-left: 15px } /* Package names */
+#manres div > ul > li > ul > li > ul > li > a { color: #000 } /* Package names */
+#manres table { margin: 10px 0; border-collapse: collapse; }
+#manres td { padding: 1px 5px; font-size: 12px; }
+#manres td + td { border-left: 1px solid #ccc }
+#manres table { margin: 2px 10px; }
+#manres table tr td:nth-child(1) { min-width: 80px }
+
+#langselect, #sectionselect { padding-left: 50px }
+#langselect a, #sectionselect a, #langselect i, #sectionselect i { padding: 3px 5px }
+
+#contents { margin: 10px 0 0 0 }
+
#footer { height: 60px; clear: both; padding: 4px 10px; color: #f8f8f8; margin: 0 0 -20px 0;
- border-top: 1px solid #888; font-size: 13px; background: url('images/gradients.png') 0 -37px repeat-x;
- -webkit-border-radius: 0 0 8px 8px; -moz-border-radius: 0 0 8px 8px; border-radius: 0 0 8px 8px; }
+ border-top: 1px solid #888; font-size: 13px; background: url('images/gradients.png') 0 -37px repeat-x; border-radius: 0 0 8px 8px; }
#footer a { font-size: 13px; padding: 0; color: #f8f8f8; }
#footer a:hover { background: none; }
@@ -106,10 +113,9 @@ pre, pre * { font-family: "Lucida Console", Monospace; font-size: 15px }
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 table { width: 100% }
#ds_box td { border: 0; padding: 1px 5px }
#ds_box i { padding-left: 5px; color: #aaa; font-style: normal }
diff --git a/www/man.js b/www/man.js
index 788523b..1632620 100644
--- a/www/man.js
+++ b/www/man.js
@@ -84,6 +84,9 @@ function setContent() {
if(arguments[i] != null)
arguments[0].appendChild(tag(arguments[i]));
}
+function getText(obj) {
+ return obj.textContent || obj.innerText || '';
+}
function setText(obj, txt) {
if(obj.textContent != null)
obj.textContent = txt;
@@ -287,227 +290,194 @@ function dsResults(hr, obj) {
+
+
/* 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');
- 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();
+(function(){
+ 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');
+ 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();
+ }
}
- }
-);
+ );
+})();
-// Efficiently pack an array of booleans into a string. (Uses something like
-// base32) Note: The resulting array after decoding may have a few more
-// elements than it had before decoding. These will be false.
-var bsCharacters = "abcdefghijklmnopqrstuvwxyz234567";
-function bsEncode(a) {
- var v = 0;
- var b = 0;
- var r = '';
- for(var i=0; i 0)
- r += bsCharacters.charAt(v<<(5-b));
- return r;
-}
+ return t;
+ };
-function bsDecode(s) {
- var a = [];
- for(var i=0; i= 97 ? 97 : 24;
- a.push(!!((n>>4)&1), !!((n>>3)&1), !!((n>>2)&1), !!((n>>1)&1), !!(n&1));
- }
- return a;
-}
+ var treeoldver = function() {
+ var lnk = this;
+ var ul = lnk;
+ var show = !lnk['data-shown'];
+ lnk['data-shown'] = show;
+ while(ul.nodeName.toLowerCase() != 'ul')
+ ul = ul.parentNode;
+ var l = ul.childNodes;
+ for(var i=0; i 0 && VARS.mans[i-1][0] == sys[0];
- if(typeof sys[4] === 'undefined')
- sys[4] = !isold;
-
- var pkgnum = 0;
- var dd = tag('dd', null);
-
- if(sys[4])
- for(var j=0; j 0)
- dl.appendChild(dd);
- }
-
- navCreateLinks(nav);
- nav.appendChild(dl);
-}
-
-
-function navCreatePkg(nav, view, dd, sys, n) {
- var pkg = sys[3][n];
-
- var isold = n > 0 && sys[3][n-1][0] == pkg[0] && sys[3][n-1][1] == pkg[1];;
- if(isold && !pkg[4])
+ setText(lnk, (show ? '- ' : '+ ')+lnk['data-hidnum']+' older versions');
return false;
+ };
- var mannum = 0;
- var pdd = tag('dd', null);
- for(var i=0; i 0)
- pdd.appendChild(tag(' '));
- pdd.appendChild(man[2] == VARS.hash ? tag('b', txt) : tag('a', {href:'/'+VARS.name+'/'+man[2]+'?v='+view}, txt));
- mannum++;
+ var treeexpand = function() {
+ var sub = byName(this.parentNode, 'ul')[0] || byName(this.parentNode, 'table')[0];
+ var exp = hasClass(sub, 'hidden');
+ setClass(sub, 'hidden', !exp);
+ setText(this, getText(this).replace(/^[^ ]+/, exp ? expanded_icon : collapsed_icon));
+ return false;
+ };
+
+ var treeitem = function(n) {
+ var icon = n.name ? (n.expand ? expanded_icon : collapsed_icon)+' ' : '';
+ return tag('li', n.hide ? {'class':'hidden', 'data-oldver':true} : {},
+ tag('a', {href:'#', onclick: treeexpand}, icon+n.name),
+ n.i ? tag('i', n.i) : null,
+ n.childs ? treelist(n.childs, n.expand ? {} : {'class':'hidden'}) : null,
+ n.table ? table(n.table, n.expand ? {} : {'class':'hidden'}) : null
+ );
+ };
+
+ var treelist = function(lst, prop) {
+ var ul = tag('ul', prop);
+ var hidden = 0;
+
+ for(i in lst) {
+ var n = lst[i];
+ if(n.hide)
+ hidden++;
+ ul.appendChild(treeitem(lst[i]));
}
- }
- if(mannum > 0) {
- dd.appendChild(tag('dt', tag('a', {href:'/pkg/'+sys[2]+'/'+pkg[0]+'/'+pkg[1]+'/'+pkg[2]}, pkg[1]),
- isold || !sys[3][n+1] || sys[3][n+1][0] != pkg[0] || sys[3][n+1][1] != pkg[1] ? null : tag('a',
- {href:'#', _pkgn: pkg[0]+'-'+pkg[1], _pkgi:n, 'class':'expand',
- title: 'Show/hide historical versions of this package',
- onclick: function() {
- for(var j=this._pkgi+1; j 0)
+ ul.appendChild(tag('li', {'class':'oldver'},
+ tag('a', {href:'#', onclick: treeoldver, 'data-hidnum':hidden}, '+ '+hidden+' older versions')
+ ));
+ return ul;
+ };
+ var clearactive = function() {
+ setClass(res, 'hidden', true);
+ var l = byName(ul, 'a');
+ for(var i=0; i 0 && VARS.mans[i-1][0] == VARS.mans[i][0])
- VARS.mans[i][4] = i > 1 && VARS.mans[i-2][0] == VARS.mans[i-1][0] ? VARS.mans[i-1][4] : !!a.shift();
- for(var i=0; i 0 && VARS.mans[i][3][j-1][0] == VARS.mans[i][3][j][0] && VARS.mans[i][3][j-1][1] == VARS.mans[i][3][j][1])
- VARS.mans[i][3][j][4] = j > 1 && VARS.mans[i][3][j-2][0] == VARS.mans[i][3][j-1][0] && VARS.mans[i][3][j-2][1] == VARS.mans[i][3][j-1][1] ? VARS.mans[i][3][j-1][4] : !!a.shift();
-}
+ res.insertBefore(tag('a', {id:'closebtn', href:'#', onclick: clearactive}, 'X'), res.firstChild);
+ res.appendChild(loading);
+ (function(){
+ var name = ul.getAttribute('data-name');
+ var hash = ul.getAttribute('data-hash');
+ var section = ul.getAttribute('data-section');
+ var locale = ul.getAttribute('data-locale');
+
+ ul.appendChild(tag('li', byId('sectionselect')
+ ? tag('a', {href:'#', onclick: buttonclick, 'data-obj': byId('sectionselect')}, 'sections')
+ : tag('i', 'sections')
+ ));
+
+ ul.appendChild(tag('li', byId('langselect')
+ ? tag('a', {href:'#', onclick: buttonclick, 'data-obj': byId('langselect')}, 'translations')
+ : tag('i', 'translations')
+ ));
+
+ ul.appendChild(tag('li', ul.getAttribute('data-hasversions') > 0
+ ? tag('a', {href:'#', onclick: buttonclick,
+ 'data-url': '/json/tree.json?name='+name+';section='+section+';locale='+locale+';cur='+hash,
+ 'data-p': 'Different versions of this manual page are available.'},
+ 'versions')
+ : tag('i', 'versions')
+ ));
+
+ ul.appendChild(tag('li', tag('a', {href:'#', onclick: buttonclick,
+ 'data-url': '/json/tree.json?hash='+hash+';name='+name+';section='+section,
+ 'data-p': 'This manual page was found in the following locations.'},
+ 'locations')));
+ })();
+})();
-if(byId('nav')) {
- navLoad(VARS.view||'');
- navCreate(byId('nav'));
-}
// The "more..." links on the homepage.
-if(byId('systems')) {
+(function(){
+ var sys = byId('systems');
+ if(!sys)
+ return;
var f = function() {
var l = byName(this.parentNode, 'a', 'hidden');
for(var i=0; i