]>
iEval git - plack-middleware-basicstyle.git/blob - lib/Plack/Middleware/BasicStyle.pm
1 package Plack
::Middleware
::BasicStyle
;
7 use parent qw
/Plack::Middleware/;
12 use Plack
::Util
::Accessor qw
/style any_content_type even_if_styled use_link_header/;
14 our $VERSION = '0.001001';
15 our $DEFAULT_STYLE = <<'EOF' =~ y/\n\t //rd;
34 $self->{link_header
} =
35 sprintf '<%s>; rel=stylesheet', $self->use_link_header
36 if $self->use_link_header;
37 $self->style($self->style // $DEFAULT_STYLE);
40 sub _content_type_ok
{
41 my ($self, $hdrs) = @_;
42 return 1 if $self->any_content_type;
44 Plack
::Util
::header_get
($hdrs, 'Content-Type');
45 return '' unless $content_type;
46 $content_type =~ m
,text
/html
,is
;
49 sub call
{ ## no critic (Complexity)
50 my ($self, $env) = @_;
51 if ($self->use_link_header) {
52 my $req = Plack
::Request
->new($env);
53 if (lc $req->path eq lc $self->use_link_header) {
54 my $days30 = 30 * 86_400
;
56 'Content-Length' => length $self->style,
57 'Content-Type' => 'text/css',
58 'Cache-Control' => "max-age=$days30",
60 return [200, \
@hdrs, [$self->style]]
64 my $res = $self->app->($env);
65 if (ref $res ne 'ARRAY'
67 || ref $res->[2] ne 'ARRAY' ) {
69 } elsif (!$self->_content_type_ok($res->[1])) {
72 my ($styled, $html_end, $head_end, $doctype_end);
73 my $parser_callback = sub {
74 my ($tagname, $offset_end, $attr) = @_;
75 $html_end //= $offset_end if $tagname eq 'html';
76 $head_end //= $offset_end if $tagname eq 'head';
77 $doctype_end //= $offset_end if $tagname eq 'doctype';
78 $styled = 1 if $tagname eq 'style';
79 $styled = 1 if $tagname eq 'link'
80 && ($attr->{rel
} // '') =~ /stylesheet/is;
83 my $p = HTML
::Parser
->new(api_version
=> 3);
84 $p->report_tags(qw
/style link html head/);
85 $p->handler(start
=> $parser_callback, 'tagname,offset_end,attr');
86 $p->handler(declaration
=> $parser_callback, 'tagname,offset_end,attr');
87 $p->parse($_) for @
{$res->[2]};
90 return $res if $styled && !$self->even_if_styled;
92 if ($self->use_link_header) {
93 push @
{$res->[1]}, 'Link', $self->{link_header
};
95 # If there's a <head>, put the style right after it
96 # Otherwise, if there's a <html>, put the style right after it
97 # Otherwise, if there's a <!DOCTYPE ...>, put the style right after it
98 # Otherwise, put the style at the very beginning of the body
99 if ($head_end || $html_end || $doctype_end) {
100 my $body = join '', @
{$res->[2]};
101 my $pos = $head_end // $html_end // $doctype_end;
102 substr $body, $pos, 0, $self->style;
105 unshift @
{$res->[2]}, $self->style
120 Plack::Middleware::BasicStyle - Add a basic <style> element to pages that don't have one
124 # Basic usage (all default options)
131 # Default options set explicitly
135 style => $Plack::Middleware::BasicStyle::DEFAULT_STYLE,
136 any_content_type => '',
137 even_if_styled => '',
138 use_link_header => '';
146 style => '<style>body { background-color: #ddd }</style>',
147 any_content_type => 1,
149 use_link_header => '/basic-style.css';
155 Plack::Middleware::BasicStyle is a Plack middleware that adds a basic
156 <style> element to HTML pages that do not have a stylesheet.
158 The default style, taken from
159 L<http://bettermotherfuckingwebsite.com>, is (before minification):
176 The middleware takes the following arguments:
182 This is the HTML fragment that will be added to unstyled pages.
184 It defaults to the value of
185 C<< $Plack::Middleware::BasicStyle::DEFAULT_STYLE >>.
187 =item B<any_content_type>
189 If true, don't check whether Content-Type contains C<text/html>.
191 If false (default), passes the response through unchanged if the
192 Content-Type header is unset or does not contain the case-insensitive
193 substring C<text/html>.
195 =item B<even_if_styled>
197 If true, don't check whether the response already includes a <style>
198 or <link ... rel="stylesheet"> element.
200 If false (default), passes the response through unchanged if the
201 response includes a <style> or <link ... rel="stylesheet"> element.
203 =item B<use_link_header>
205 If false or unset (default), the given HTML fragment will be added
206 right after the <head> start tag (if this exists), right after the
207 <html> start tag (if this exists but <head> doesn't), or at the
208 beginning of the document (if neither <html> nor <head> exists).
210 If set, its value is interpreted as an URL path. The body of the
211 response will not be modified, instead a C<Link:> HTTP header will be
212 added to unstyled pages. Additionally, the middleware will intercept
213 requests to that exact URL path and return the style (with status 200,
214 a Content-Type of C<text/css>, a correct Content-Length header, and a
215 Cache-Control header instructing the browser to cache the style for 30
218 Setting this makes the module more resilient to bugs and more
219 efficient at the cost of asking the client to make an extra request.
220 Therefore setting this argument is B<recommended>.
226 This middleware only works with simple (non-streaming) responses,
227 where the body is an arrayref.
229 In other words, responses where the body is an IO::Handle, or
230 streaming/delayed responses are NOT supported and will be passed
231 through unchanged by this middleware.
235 L<http://bettermotherfuckingwebsite.com>
239 Marius Gavrilescu, E<lt>marius@ieval.roE<gt>
241 =head1 COPYRIGHT AND LICENSE
243 Copyright (C) 2016-2017 by Marius Gavrilescu
245 This library is free software; you can redistribute it and/or modify
246 it under the same terms as Perl itself, either Perl version 5.24.0 or,
247 at your option, any later version of Perl 5 you may have available.
This page took 0.070782 seconds and 5 git commands to generate.