File Coverage

blib/lib/Web/PerlDistSite/MenuItem.pm
Criterion Covered Total %
statement 18 105 17.1
branch 0 36 0.0
condition 0 22 0.0
subroutine 6 17 35.2
pod 0 7 0.0
total 24 187 12.8


line stmt bran cond sub pod time code
1             package Web::PerlDistSite::MenuItem;
2:
use utf8;
3:
4: our $VERSION = '0.001007';
5:
6: use Moo;
7: use Web::PerlDistSite::Common -lexical, -all;
8:
9: use HTML::HTML5::Writer;
10: use HTML::HTML5::Sanity;
11: use XML::LibXML::PrettyPrint;
12:
13: has project => (
14: is => 'rw',
15: isa => Object,
16: weak_ref => true,
17: trigger => sub ( $self, $new_val, $old_val=undef ) {
18: $_->project( $new_val ) for $self->children->@*;
19: },
20: );
21:
22: has name => (
23: is => 'ro',
24: isa => Str,
25: required => true,
26: );
27:
28: has title => (
29: is => 'ro',
30: isa => Str,
31: required => true,
32: );
33:
34: has href => (
35: is => 'lazy',
36: isa => Str,
37: builder => true,
38: );
39:
40: has rel => (
41: is => 'ro',
42: isa => Str,
43: default => 'related',
44: );
45:
46: has target => (
47: is => 'ro',
48: isa => Str,
49: default => '_self',
50: );
51:
52: has icon => (
53: is => 'ro',
54: isa => Str,
55: );
56:
57: has children => (
58: is => 'rw',
59: isa => ArrayRef->of(
60: InstanceOf
61: ->of( 'Web::PerlDistSite::MenuItem' )
62: ->plus_constructors( HashRef, 'from_hashref' )
63: ),
64: coerce => true,
65: default => sub { [] },
66: );
67:
68: sub from_hashref ( $class, $hashref ) {
69:
70: if ( exists $hashref->{divider} ) {
71: $class .= '::Divider';
72: }
73: elsif ( exists $hashref->{pod} ) {
74: $class .= '::Pod';
75: }
76: elsif ( exists $hashref->{source} and $hashref->{source} =~ /.pod/ ) {
77: $class .= '::PodFile';
78: }
79: elsif ( exists $hashref->{source} and $hashref->{source} =~ /.md/ ) {
80: $class .= '::MarkdownFile';
81: }
82: elsif ( exists $hashref->{source} and $hashref->{source} =~ /.html/ ) {
83: $class .= '::HTMLFile';
84: }
85:
86: return Module::Runtime::use_module( $class )->new( $hashref );
87: }
88:
89: sub system_path ( $self ) {
90: path( $self->project->dist_dir )->child( $self->name . '.html' )
91: }
92:
93: sub write_page ( $self ) {
94: return $self;
95: }
96:
97: sub write_pages ( $self ) {
98: $self->write_page;
99: $_->write_pages for $self->children->@*;
100: }
101:
102: sub _build_href ( $self ) {
103: if ( $self->name ) {
104: if ( $self->name eq 'github' ) {
105: return $self->project->github;
106: }
107: if ( $self->name eq 'metacpan' ) {
108: return 'https://metacpan.org/dist/' . $self->project->name;
109: }
110: if ( $self->name eq 'issues' ) {
111: return $self->project->issues // ( $self->project->github . '/issues' );
112: }
113: }
114: return $self->project->root_url . $self->name . '.html';
115: }
116:
117: sub _compile_dom ( $self, $dom ) {
118: state $p = do {
119: my $pp = XML::LibXML::PrettyPrint->new;
120: push $pp->{element}{preserves_whitespace}->@*, sub ( $node ) {
121: return undef unless $node->can( 'tagName' );
122: return 1 if $node->tagName eq 'code' and $node->parentNode->tagName eq 'pre';
123: return undef;
124: };
125: unshift $pp->{element}{inline}->@*, sub ( $node ) {
126: return undef unless $node->can( 'tagName' );
127: return 1 if $node->tagName eq 'pre' and $node->getElementsByTagName( 'code' )->size;
128: return undef;
129: };
130: unshift $pp->{element}{compact}->@*, 'a';
131: $pp;
132: };
133: state $w = HTML::HTML5::Writer->new( markup => 'xhtml', polyglot => true );
134: my $sane = fix_document( $dom );
135: $p->pretty_print( $sane );
136: return $w->document( $sane );
137: }
138:
139: sub nav_item ( $self, $active_item ) {
140: my $icon = $self->icon // '';
141: if ( length $icon ) {
142: $icon .= ' ';
143: }
144:
145: if ( $self->children->@* ) {
146: my @items = map $_->dropdown_item( $active_item ), $self->children->@*;
147: return sprintf(
148: '<li class="nav-item dropdown"><a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">%s%s</a><ul class="dropdown-menu">%s</ul></li>',
149: $icon,
150: esc_html( $self->title ),
151: join( q{}, @items ),
152: );
153: }
154: elsif ( $self == $active_item ) {
155: return sprintf(
156: '<li class="nav-item"><a class="nav-link active" rel="%s" target="%s" href="%s">%s%s</a></li>',
157: esc_html( $self->rel ),
158: esc_html( $self->target ),
159: esc_html( $self->href ),
160: $icon,
161: esc_html( $self->title ),
162: );
163: }
164: else {
165: return sprintf(
166: '<li class="nav-item"><a class="nav-link" rel="%s" target="%s" href="%s">%s%s</a></li>',
167: esc_html( $self->rel ),
168: esc_html( $self->target ),
169: esc_html( $self->href ),
170: $icon,
171: esc_html( $self->title ),
172: );
173: }
174: }
175:
176: sub dropdown_item ( $self, $active_item ) {
177: my $icon = $self->icon // '';
178: if ( length $icon ) {
179: $icon .= ' ';
180: }
181:
182: if ( $self == $active_item ) {
183: return sprintf(
184: '<li><a class="dropdown-item active" rel="%s" target="%s" href="%s">%s%s</a></li>',
185: esc_html( $self->rel ),
186: esc_html( $self->target ),
187: esc_html( $self->href ),
188: $icon,
189: esc_html( $self->title ),
190: );
191: }
192: else {
193: return sprintf(
194: '<li><a class="dropdown-item" rel="%s" target="%s" href="%s">%s%s</a></li>',
195: esc_html( $self->rel ),
196: esc_html( $self->target ),
197: esc_html( $self->href ),
198: $icon,
199: esc_html( $self->title ),
200: );
201: }
202: }
203:
204: sub page_title ( $self ) {
205: return sprintf( '%s — %s', $self->project->name, $self->title );
206: }
207:
208: 1;
209: