in reply to Running local Perl from HTML

you can't edit the table directly in the browser

Sure you can, JavaScript makes that possible. <plug type="shameless"> It's even possible to run Perl instead of JS in the browser with WebPerl. </plug>

I also agree with what choroba said about running a webserver locally. You can bind the webserver to your localhost IP, and if you wanted to play it extra safe (like on a multi-user machine), you could use HTTPS and pass an access token.

The following uses Mojolicious to serve up a HTML file, injecting some JavaScript in the process. This JavaScript reacts to clicks on <td> and <th> elements, allowing you to edit their text, and then it immediately POSTs the modified HTML back to the server, which then rewrites the HTML file with what the browser sent. This is admittedly not the most elegant way to go about it, but it should work. Note it only supports one client at a time: although the JS makes sure to only send one request at a time, if two browser windows were to be open and submit requests at the same time, one of the changes would get lost.

#!/usr/bin/env perl use Mojolicious::Lite; use Mojo::Util qw/md5_sum/; use Mojo::DOM; use File::Replace 'replace3'; # Start me with: morbo -l https://localhost:3000/ my $HTMLFILE = '/tmp/test.html'; # ##### For Demo only: Write a default HTML file ##### if (!-e $HTMLFILE) { open my $fh, '>:raw:encoding(UTF-8)', $HTMLFILE or die $!; print $fh <<'END_HTML'; <!DOCTYPE html> <html lang="en"> <head> <title>Hello, World!</title> <style> table { border-collapse: collapse; } table, th, td { border: 1px solid black; } th, td { padding: 0.1em 0.4em; } </style> </head> <body> <table border="1"><tbody> <tr> <th>Foo</th> <th>Bar</th> <th>Quz</th> </tr> <tr> <td>Hello</td> <td>Perl</td> <td>World</td> </tr> <tr> <td>123</td> <td>456</td> <td>789</td> </tr> </tbody></table> </body> </html> END_HTML close $fh; } # ##### Authentication Stuff (optional) ##### my $TOKEN = md5_sum(rand(1e15).time); hook before_server_start => sub { my ($server, $app) = @_; print "URL with Token: ", $_->query(token=>$TOKEN), "\n" for map {Mojo::URL->new($_)} @{$server->listen}; }; under sub { my $c = shift; return 1 if ($c->param('token')//'') eq $TOKEN; $c->render(text => 'Bad token!', status => 403); return undef; }; # ##### JS Code ##### # note: the jQuery JS could also be saved locally my $JSCODE = <<'END_JSCODE'; <script src="" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script> <script> $(function() { var requestInProgress = false; $('th, td').click(function() { if (requestInProgress) return; var newtxt = prompt("Foo", $(this).text() ); if ( newtxt==null ) return; $(this).text( newtxt ); requestInProgress = true; $.ajax({ method: 'POST', url: 'save?token=<<<TOKEN>>>', contentType: 'text/plain', data: document.body.innerHTML, dataType: 'text', }) .done(function(data) { console.log(data) }) .fail(function(jqXHR, textStatus, errorThrown) { alert("Error when saving: "+textStatus+"\n"+errorThrown); }) .always(function() { requestInProgress = false; }); }); }); </script> END_JSCODE $JSCODE=~s/<<<TOKEN>>>/$TOKEN/g; # ##### Request Handling Stuff ##### get '/' => sub { # read the file and inject our JavaScript my $c = shift; open my $fh, '<:raw:encoding(UTF-8)', $HTMLFILE or die $!; my $dom = Mojo::DOM->new(do { local $/; <$fh> }); close $fh; $dom->at('head')->append_content($JSCODE); $c->render(text => $dom); }; # NOTE this does not handle multiple requests at once. The JS tries to # prevent this, but it can still happen if there are multiple clients. post '/save' => sub { # rewrite the HTML file my $c = shift; my ($ifh,$ofh,$rpl) = replace3($HTMLFILE, ':raw:encoding(UTF-8)'); my $dom = Mojo::DOM->new(do { local $/; <$ifh> }); # Firefox apparently inserts blank lines here?? Remove them... ( my $newbody = $c->req->body ) =~ s/\n\K\n+\z//; $dom->at('body')->content($newbody); print $ofh $dom->to_string; $rpl->finish; $c->render(text=>'saved successfully'); }; app->start;

Update: Disclaimer: File::Replace is one of my modules too.