File Coverage

blib/lib/Mojolicious/Plugin/HTMX.pm
Criterion Covered Total %
statement 84 107 78.5
branch 17 34 50.0
condition 2 7 28.5
subroutine 20 32 62.5
pod 1 1 100.0
total 124 181 68.5


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::HTMX;
2 2     2   2398 use Mojo::Base 'Mojolicious::Plugin';
  2         224318  
  2         16  
3              
4 2     2   1827 use Mojo::ByteStream;
  2         3959  
  2         99  
5 2     2   528 use Mojo::JSON qw(encode_json decode_json);
  2         19160  
  2         226  
6              
7             our $VERSION = '1.01';
8              
9             my @HX_RESWAPS = (qw[
10             innerHTML
11             outerHTML
12             beforebegin
13             afterbegin
14             beforeend
15             afterend
16             delete
17             none
18             ]);
19              
20 2     2   17 use constant HX_TRUE => 'true';
  2         6  
  2         156  
21 2     2   12 use constant HX_FALSE => 'false';
  2         4  
  2         105  
22              
23 2     2   24 use constant HTMX_STOP_POLLING => 286;
  2         10  
  2         97  
24 2     2   11 use constant HTMX_CDN_URL => 'https://unpkg.com/htmx.org';
  2         4  
  2         3762  
25              
26             sub register {
27              
28 1     1 1 56 my ($self, $app) = @_;
29              
30 1         14 $app->helper('htmx.asset' => \&_htmx_js);
31 1     0   505 $app->helper('is_htmx_request' => sub { _header(shift, 'HX-Request', HX_TRUE) });
  0         0  
32              
33 1     0   89 $app->helper('htmx.req.boosted' => sub { _header(shift, 'HX-Boosted', HX_TRUE) });
  0         0  
34 1     0   642 $app->helper('htmx.req.current_url' => sub { Mojo::URL->new(_header(shift, 'HX-Current-URL')) });
  0         0  
35 1     0   633 $app->helper('htmx.req.history_restore_request' => sub { _header(shift, 'HX-History-Restore-Request', HX_TRUE) });
  0         0  
36 1     0   684 $app->helper('htmx.req.prompt' => sub { _header(shift, 'HX-Prompt') });
  0         0  
37 1     0   759 $app->helper('htmx.req.request' => sub { _header(shift, 'HX-Request', HX_TRUE) });
  0         0  
38 1     0   794 $app->helper('htmx.req.target' => sub { _header(shift, 'HX-Target') });
  0         0  
39 1     0   898 $app->helper('htmx.req.trigger_name' => sub { _header(shift, 'HX-Trigger-Name') });
  0         0  
40 1     0   908 $app->helper('htmx.req.trigger' => sub { _header(shift, 'HX-Trigger') });
  0         0  
41              
42             $app->helper(
43             'htmx.req.triggering_event' => sub {
44 0 0   0   0 eval { decode_json(_header(shift, 'Triggering-Event')) } || {};
  0         0  
45             }
46 1         980 );
47              
48 1         1043 $app->helper('htmx.res.location' => \&_res_location);
49 1         1362 $app->helper('htmx.res.push_url' => \&_res_push_url);
50 1         1359 $app->helper('htmx.res.redirect' => \&_res_redirect);
51 1         1445 $app->helper('htmx.res.refresh' => \&_res_refresh);
52 1         1519 $app->helper('htmx.res.replace_url' => \&_res_replace_url);
53 1         1534 $app->helper('htmx.res.reswap' => \&_res_reswap);
54 1         1608 $app->helper('htmx.res.reselect' => \&_res_reselect);
55 1         1786 $app->helper('htmx.res.retarget' => \&_res_retarget);
56              
57 1     3   1728 $app->helper('htmx.res.trigger' => sub { _res_trigger('default', @_) });
  3         27887  
58 1     3   1816 $app->helper('htmx.res.trigger_after_settle' => sub { _res_trigger('after_settle', @_) });
  3         28281  
59 1     3   1834 $app->helper('htmx.res.trigger_after_swap' => sub { _res_trigger('after_swap', @_) });
  3         28043  
60              
61             }
62              
63             sub _htmx_js {
64              
65 0     0   0 my ($self, %params) = @_;
66 0   0     0 my $url = delete $params{url} || HTMX_CDN_URL;
67 0         0 my $ext = delete $params{ext};
68              
69 0 0       0 if ($ext) {
70 0         0 $url .= "/dist/ext/$ext.js";
71             }
72              
73 0         0 return Mojo::ByteStream->new(Mojo::DOM::HTML::tag_to_html('script', 'src' => $url));
74              
75             }
76              
77             sub _header {
78              
79 0     0   0 my ($c, $header, $check) = @_;
80 0         0 my $value = $c->req->headers->header($header);
81              
82 0 0 0     0 if ($value && $check) {
83 0 0       0 return 1 if ($value eq $check);
84 0         0 return 0;
85             }
86              
87 0         0 return $value;
88              
89             }
90              
91             sub _res_location {
92              
93 3     3   42120 my $c = shift;
94 3 100       13 my $location = (@_ > 1) ? {@_} : $_[0];
95              
96 3 50       25 return undef unless $location;
97              
98 3 100       9 if (ref $location eq 'HASH') {
99 2         16 $location = encode_json($location);
100             }
101              
102 3         202 return $c->res->headers->header('HX-Location' => $location);
103              
104             }
105              
106             sub _res_push_url {
107              
108 2     2   19789 my ($c, $push_url) = @_;
109 2 50       7 return undef unless $push_url;
110              
111 2         14 return $c->res->headers->header('HX-Push-Url' => $push_url);
112              
113             }
114              
115             sub _res_redirect {
116              
117 2     2   20120 my ($c, $redirect) = @_;
118 2 50       6 return undef unless $redirect;
119              
120 2         19 return $c->res->headers->header('HX-Redirect' => $redirect);
121              
122             }
123              
124             sub _res_refresh {
125 1     1   11569 my ($c) = @_;
126 1         5 return $c->res->headers->header('HX-Refresh' => HX_TRUE);
127             }
128              
129             sub _res_replace_url {
130              
131 2     2   18441 my ($c, $replace_url) = @_;
132 2 50       7 return undef unless $replace_url;
133              
134 2         7 return $c->res->headers->header('HX-Replace-Url' => $replace_url);
135              
136             }
137              
138             sub _res_reswap {
139              
140 16     16   144615 my ($c, $reswap) = @_;
141 16 50       46 return undef unless $reswap;
142              
143 16         40 my $is_reswap = grep {/^$reswap$/} @HX_RESWAPS;
  128         588  
144 16 50       47 Carp::croak "Unknown reswap value" if (!$is_reswap);
145              
146 16         55 return $c->res->headers->header('HX-Reswap' => $reswap);
147              
148             }
149              
150             sub _res_reselect {
151              
152 2     2   19906 my ($c, $reselect) = @_;
153 2 50       8 return undef unless $reselect;
154              
155 2         22 return $c->res->headers->header('HX-Reselect' => $reselect);
156              
157             }
158              
159             sub _res_retarget {
160              
161 2     2   18945 my ($c, $retarget) = @_;
162 2 50       7 return undef unless $retarget;
163              
164 2         8 return $c->res->headers->header('HX-Retarget' => $retarget);
165              
166             }
167              
168             sub _res_trigger {
169              
170 9     9   23 my ($type, $c) = (shift, shift);
171 9 100       38 my $trigger = (@_ > 1) ? {@_} : $_[0];
172              
173 9 50       28 return undef unless $trigger;
174              
175 9         28 my $trigger_header = {after_settle => 'HX-Trigger-After-Settle', after_swap => 'HX-Trigger-After-Swap'};
176              
177 9 100       29 if (ref $trigger eq 'HASH') {
178 6         35 $trigger = encode_json($trigger);
179             }
180              
181 9   100     381 my $header = $trigger_header->{$type} || 'HX-Trigger';
182              
183 9         35 return $c->res->headers->header($header => $trigger);
184              
185             }
186              
187             1;
188              
189             =encoding utf8
190              
191             =head1 NAME
192              
193             Mojolicious::Plugin::HTMX - Mojolicious Plugin for htmx
194              
195             =head1 SYNOPSIS
196              
197             # Mojolicious
198             $self->plugin('Mojolicious::Plugin::HTMX');
199              
200             # Mojolicious::Lite
201             plugin 'Mojolicious::Plugin::HTMX';
202              
203             get '/trigger' => 'trigger';
204             post '/trigger' => sub ($c) {
205              
206             state $count = 0;
207             $count++;
208              
209             $c->htmx->res->trigger(showMessage => 'Here Is A Message');
210             $c->render(text => "Triggered $count times");
211              
212             };
213              
214             @@ template.html.ep
215            
216            
217             %= app->htmx->asset
218            
219            
220             %= content
221            
222            
223              
224             @@ trigger.html.ep
225             % layout 'default';
226            

Trigger

227              
228            
229              
230            
235              
236             =head1 DESCRIPTION
237              
238             L is a L plugin to add htmx in your Mojolicious application.
239              
240             =head1 HELPERS
241              
242             L implements the following helpers.
243              
244             =head2 GENERIC HELPERS
245              
246             =head3 htmx->asset
247              
248             %= htmx->asset
249             %= htmx->asset(src => '/assets/js/htmx.min.js')
250             %= htmx->asset(ext => debug)
251              
252             Generate C