Skip to content

Commit

Permalink
split table of contents from pod content
Browse files Browse the repository at this point in the history
Split the index from the rest of the pod content, allowing it to be
templated differently. This allows its markup to be included with the
static content, rather than being assembled in javascript.

This should make it easier to format it differently in the future, and
should also fix the scroll position changing on page load.
  • Loading branch information
haarg committed Jan 20, 2024
1 parent 0f9f355 commit ae763a5
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 119 deletions.
1 change: 1 addition & 0 deletions lib/MetaCPAN/Web/Controller/Pod.pm
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ sub view : Private {
canonical => $canonical,
documented_module => $documented_module,
pod => $pod->{pod_html},
pod_index => $pod->{pod_index},
template => 'pod.tx',
} );

Expand Down
24 changes: 17 additions & 7 deletions lib/MetaCPAN/Web/Controller/Pod2HTML.pm
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,30 @@ sub pod2html : Path : Args(0) {
show_errors => 1,
}
)->get;
my $html = $pod_data->{pod_html} // q{};
my $html = $pod_data->{pod_html} // q{};
my $index = $pod_data->{pod_index} // q{};

my $results = {
pod => $pod,
pod_rendered => $html,
pod_name => $pod_data->{pod_name},
abstract => $pod_data->{abstract},
pod => $pod,
pod_html => $html,
pod_index => $index,
pod_name => $pod_data->{pod_name},
abstract => $pod_data->{abstract},
};
if ( $c->req->parameters->{raw} ) {
$c->res->content_type('text/html');
$c->res->body($html);
$c->res->body( '<nav class="toc">' . $index . '</nav>' . $html );
$c->detach;
}
$c->stash($results);
elsif ( $c->req->accepts('application/json') ) {
$c->stash( {
current_view => 'JSON',
json => $results,
} );
}
else {
$c->stash($results);
}
}

__PACKAGE__->meta->make_immutable;
Expand Down
2 changes: 2 additions & 0 deletions lib/MetaCPAN/Web/Model/API/Author.pm
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ sub get {

sub get_multiple {
my ( $self, @authors ) = @_;
return Future->done( { took => 0, total => 0, authors => [] } )
if !@authors;
return $self->request( '/author/by_ids', { id => [ map uc, @authors ] } )
->transform(
done => sub {
Expand Down
7 changes: 5 additions & 2 deletions lib/MetaCPAN/Web/Model/API/Pod.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package MetaCPAN::Web::Model::API::Pod;
use Moose;
use namespace::autoclean;

use MetaCPAN::Web::RenderUtil qw( filter_html );
use MetaCPAN::Web::RenderUtil qw( split_index filter_html );
use Encode qw( encode );
use Future ();

Expand Down Expand Up @@ -75,8 +75,11 @@ sub _with_filter {
sub {
my $data = shift;
if ( my $raw = $data->{raw} ) {
my ( $index, $body ) = split_index($raw);
$data->{pod_index}
= filter_html( $index, $data->{path} ? $data : () );
$data->{pod_html}
= filter_html( $raw, $data->{path} ? $data : () );
= filter_html( $body, $data->{path} ? $data : () );
}
return Future->done($data);
};
Expand Down
17 changes: 16 additions & 1 deletion lib/MetaCPAN/Web/RenderUtil.pm
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use Digest::MD5 ();
our @EXPORT_OK = qw(
filter_html
gravatar_image
split_index
);

sub filter_html {
Expand Down Expand Up @@ -41,6 +42,7 @@ sub filter_html {
i => [],
li => ['id'],
ol => [],
nav => [],
p => [],
pre => [ {
class => qr/^line-numbers$/,
Expand All @@ -59,7 +61,7 @@ sub filter_html {
td => [qw( colspan rowspan )],
tr => [],
u => [],
ul => [ { id => qr/^index$/ } ],
ul => [],

#
# SVG tags.
Expand Down Expand Up @@ -146,4 +148,17 @@ sub gravatar_image {
$grav_id, $size // 80;
}

sub split_index {
my ($html) = @_;

# this will hopefully be done by the API in the future
$html =~ s{\A<ul id="index">(.*?^</ul>\n?)}{<nav><ul>$1</nav>}ms;

# both of these regexes are kind of ugly, but we know the content produced
# by the API, so it should still work fine.
$html =~ s{\A<nav>(.*?)</nav>\n*}{}s;
my $pod_index = $1;
return ( $pod_index, $html );
}

1;
7 changes: 6 additions & 1 deletion root/pod.tx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,13 @@
</li>
%% }
%% override page_content -> {
%% if $pod_index {
<nav class="toc">
<div class="toc-header"><strong>Contents</strong></div>
%% $pod_index | mark_raw;
</nav>
%% }
<div class="pod anchors">
<a name="___pod"></a>
%% if $pod {
%% $pod | mark_raw;
%% }
Expand Down
7 changes: 6 additions & 1 deletion root/pod2html/pod2html.tx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
</div>
</div>
</div>
<div id="metacpan-pod-renderer-output" class="pod anchors" [% if !$pod_rendered { %]style="display: none"[% } %]>[% $pod_rendered | raw %]</div>
<div id="metacpan-pod-renderer-output" class="pod anchors" [% if !$pod_html { %]style="display: none"[% } %]>
<nav class="toc">
<div class="toc-header"><strong>Contents</strong></div>
[% $pod_index %]
</nav>
[% $pod_html | raw %]
</div>
%% }
115 changes: 61 additions & 54 deletions root/static/js/cpan.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,32 +51,6 @@ function togglePanel(side, visible) {
return false;
}

function toggleTOC() {
var container = $('#index-container');
if (container.length == 0) return false;
var visible = !container.hasClass('hide-index');
var index = $('#index');
var newHeight = 0;
if (!visible) {
newHeight = index.get(0).scrollHeight;
}
index.animate({
height: newHeight
}, {
duration: 200,
complete: function() {
if (newHeight > 0) {
index.css({
height: 'auto'
});
}
}
});
MetaCPAN.storage.setItem('hideTOC', (visible ? 1 : 0));
container.toggleClass('hide-index');
return false;
}

function setFavTitle(button) {
button.attr('title', button.hasClass('active') ? 'Remove from favorites' : 'Add to favorites');
return;
Expand Down Expand Up @@ -298,25 +272,54 @@ $(document).ready(function() {

$('.dropdown-toggle').dropdown();

function format_index(index) {
index.wrap('<div id="index-container"><div class="index-border"></div></div>');
var container = index.parent().parent();
function format_toc(toc) {
'use strict';
if (MetaCPAN.storage.getItem('hideTOC') == 1) {
toc.classList.add("hide-toc");
}

var index_hidden = MetaCPAN.storage.getItem('hideTOC') == 1;
index.before(
'<div class="index-header"><b>Contents</b>' + ' [ <button class="btn-link toggle-index"><span class="toggle-show">show</span><span class="toggle-hide">hide</span></button> ] </div>');
const toc_header = toc.querySelector('.toc-header');
const toc_body = toc.querySelector('ul');

$('.toggle-index').on('click', function(e) {
toc_header.insertAdjacentHTML('beforeend',
' [ <button class="btn-link toggle-toc"><span class="toggle-show">show</span><span class="toggle-hide">hide</span></button> ]'
);
toc_header.querySelector('.toggle-toc').addEventListener('click', e => {
e.preventDefault();
toggleTOC();
const currentVisible = !toc.classList.contains('hide-toc');
MetaCPAN.storage.setItem('hideTOC', currentVisible ? 1 : 0);

const fullHeight = toc_body.scrollHeight;

if (currentVisible) {
const trans = toc_body.style.transition;
toc_body.style.transition = '';

requestAnimationFrame(() => {
toc_body.style.height = fullHeight + 'px';
toc_body.style.transition = trans;
toc.classList.toggle('hide-toc');

requestAnimationFrame(() => {
toc_body.style.height = null;
});
});
} else {
const finish = e => {
toc_body.removeEventListener('transitionend', finish);
toc_body.style.height = null;
};

toc_body.addEventListener('transitionend', finish);
toc_body.style.height = fullHeight + 'px';
toc.classList.toggle('hide-toc');
}
});
if (index_hidden) {
container.addClass("hide-index");
}
}
var index = $("#index");
if (index.length) {
format_index(index);

let toc = document.querySelector(".content .toc")
if (toc) {
format_toc(toc);
}

$('a[href*="/search?"]').on('click', function() {
Expand Down Expand Up @@ -366,24 +369,28 @@ $(document).ready(function() {
url: '/pod2html',
method: 'POST',
data: {
pod: pod,
raw: true
pod: pod
},
headers: {
Accept: "application/json"
},
success: function(data, stat, req) {
rendered.html(data);
loading.hide();
error.hide();
var res = $('#NAME + p').text().match(/^([^-]+?)\s*-\s*(.*)/);
if (res) {
var title = res[0];
var abstract = res[1];
document.title = "Pod Renderer - " + title + " - metacpan.org";
}
var index = $("#index", rendered);
if (index.length) {
format_index(index);
var title = data.pod_title;
var abstract = data.pod_abstract;
document.title = "Pod Renderer - " + title + " - metacpan.org";
rendered.html(
'<nav class="toc"><div class="toc-header"><strong>Contents</strong></div>' +
data.pod_index +
'</nav>' +
data.pod_html
);
var toc = $("nav", rendered);
if (toc.length) {
format_toc(toc[0]);
}
create_anchors(rendered);
loading.hide();
error.hide();
rendered.show();
submit.removeAttr("disabled");
},
Expand Down
Loading

0 comments on commit ae763a5

Please sign in to comment.