Merge branch 'gh-pages' into pages_for_v0.09-beta

Intermediate sync of the branches
gh-pages
Hauke D 7 years ago
commit 58bdcd9b9b

@ -20,11 +20,11 @@ with the following differences:
(see import_regex_tester.pl)
ENDMSG
( $html =~ s{ <(?<x>script\s+src="webperl\.js"\s*></script)> }{<!--$+{x}-->}xms )==1 or die;
( $html =~ s{ <!--(?<x>script\s+src="http.+?webperl\.js"[^>]+crossorigin[^>]+></script)--> }{<$+{x}>}xms )==1 or die;
( $html =~ s{ ^ \s* our \s+ \$RUN_CODE_IN_IFRAME\s*=\s*\K0(?=\s*;\s*) }{1}xms )==1 or die;
( $html =~ s{ https?://github.com/haukex/webperl/blob/\Kmaster/web/regex_tester.html }{gh-pages/regex.html}xms )==1 or die;
( $html =~ s{ \#\#\#\#\#\s*-->\n\K }{\n<!-- $msg-->\n}xms )==1 or die;
( $html =~ s{ <(?<x>script\s+src="(?:webperl\.js|__WEBPERLURL__)"\s*></scr_*ipt)> }{<!--$+{x}-->}xmsg )==2 or die;
( $html =~ s{ <!--(?<x>script\s+src="http.+?webperl\.js"[^>]+crossorigin[^>]+></scr_*ipt)--> }{<$+{x}>}xmsg )==2 or die;
( $html =~ s{ ^ \s* our \s+ \$RUN_CODE_IN_IFRAME\s*=\s*\K[01](?=\s*;\s*) }{1}xmsg )==1 or die;
( $html =~ s{ https?://github.com/haukex/webperl/blob/\Kmaster/web/regex_tester.html }{gh-pages/regex.html}xmsg )==1 or die;
( $html =~ s{ \#\#\#\#\#\s*-->\n\K }{\n<!-- $msg-->\n}xmsg )==1 or die;
open my $ofh, '>:encoding(UTF-8)', $outfn or die "$outfn: $!";
print $ofh $html;

@ -5,8 +5,8 @@ Building -
[Legal](legal.html) -
[GitHub Wiki](https://github.com/haukex/webperl/wiki/Building-WebPerl) \]
🕸️🐪 Building WebPerl
===================
Building WebPerl
================
**Notice: WebPerl is very much in beta.**
@ -48,6 +48,15 @@ The source code is in two repositories:
You only need to check out the first of the two, the `emperl5` repository
is checked out by the build script.
Some of the central source files of WebPerl are:
- [`webperl/build/build.pl`](https://github.com/haukex/webperl/blob/master/build/build.pl)
- [`emperl5/hints/emscripten.sh`](https://github.com/haukex/emperl5/blob/emperl_v5.28.0/hints/emscripten.sh)
- [`emperl5/perlmain_noexit_patch`](https://github.com/haukex/emperl5/blob/emperl_v5.28.0/perlmain_noexit_patch)
- [`emperl5/ext/WebPerl/WebPerl.xs`](https://github.com/haukex/emperl5/blob/emperl_v5.28.0/ext/WebPerl/WebPerl.xs)
- [`emperl5/ext/WebPerl/WebPerl.pm`](https://github.com/haukex/emperl5/blob/emperl_v5.28.0/ext/WebPerl/WebPerl.pm)
- [`webperl/web/webperl.js`](https://github.com/haukex/webperl/blob/master/web/webperl.js)
Running the Build
-----------------
@ -157,6 +166,9 @@ so for example `Cpanel/JSON/XS` instead of `Cpanel::JSON::XS`
Note that the build script does **not** automatically fetch modules'
dependencies, for now you will need to resolve them and add them to
`EMPERL_EXTENSIONS` yourself. (This may be improved upon in the future.)
You can try out the script
[`experiments/depend.pl`](https://github.com/haukex/webperl/blob/master/experiments/depend.pl),
which uses the MetaCPAN API to resolve dependencies.
***

@ -0,0 +1,208 @@
<!doctype html>
<html lang="en-us">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>WebPerl Code Demo</title>
<!-- ##### WebPerl - http://webperl.zero-g.net #####
Copyright (c) 2018 Hauke Daempfling (haukex@zero-g.net)
at the Leibniz Institute of Freshwater Ecology and Inland Fisheries (IGB),
Berlin, Germany, http://www.igb-berlin.de
This program is free software; you can redistribute it and/or modify
it under the same terms as Perl 5 itself: either the GNU General Public
License as published by the Free Software Foundation (either version 1,
or, at your option, any later version), or the "Artistic License" which
comes with Perl 5.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the licenses for details.
You should have received a copy of the licenses along with this program.
If not, see http://perldoc.perl.org/index-licence.html
##### -->
<style>
p {
font-family: Calibri, Ubuntu, "Droid Sans", Tahoma, Arial, Helvetica, sans-serif;
}
pre,textarea,code {
font-family: Consolas, "Ubuntu Mono", "Droid Sans Mono", "Lucida Console", "Courier New", Courier, monospace;
}
iframe.perleditor {
display: block;
border: 1px dotted lightgrey;
width: 100%;
max-width: 50em;
margin: 0.2em 0;
}
</style>
<!-- Optional "IFrame Resizer": -->
<!--cacheable--><script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/3.6.2/iframeResizer.min.js" integrity="sha256-aYf0FZGWqOuKNPJ4HkmnMZeODgj3DVslnYf+8dCN9/k=" crossorigin="anonymous"></script>
</head>
<body>
<p>
This page demonstrates the embeddable
<strong><a href="http://webperl.zero-g.net" target="_blank">WebPerl</a>
Code Demo Editor</strong> (beta), which can be embedded using <code>&lt;iframe&gt;</code> elements, including
<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox" target="_blank">sandboxing</a>.
The documentation is contained in the source of this page, please use
the "View Source" function of your browser to view it, or have a look at
<a href="https://github.com/haukex/webperl/tree/master/web/democode"
target="_blank">the project sources on GitHub</a>.
</p>
<!-- Thank you to LanX from PerlMonks for the inspiration! :-)
https://www.perlmonks.org/?node_id=1223812 -->
<!-- First, you have to include the following hidden IFrame, which
loads the "Perl runner". This is (currently) necessary because this
IFrame needs to re-load itself in order to re-run Perl. This IFrame
*must* have the "name='perlrunner'" attribute and must be embedded at
the same level as the Perl editor IFrame(s). The frames communicate
via the "window.postMessage()" mechanism, which is safe for
cross-origin communications and sandboxing. Currently, in order to
conserve memory, a single runner serves multiple "clients", that is,
the "editor" IFrames below.
It is also possible to link to perleditor.html directly: if it
detects that it is not running in an IFrame, it will load the runner
on its own (after a very brief delay).
-->
<iframe name="perlrunner" sandbox="allow-scripts" src="perlrunner.html" style="display:none;"></iframe>
<p>This is a simple example of running a oneliner:</p>
<!-- The following is a basic example showing a single input file and
Perl oneliner.
All files are currently always encoded as UTF-8, which is why the
"-CSD" switch is used below. This is not strictly necessary when the
input files are pure ASCII, but it is important to remember that Perl
does *not* default to UTF-8. Reading/writing binary data via the
editor and runner is currently *not* supported.
Standard input/output redirection is currently not supported. It is
also currently not supported to supply STDIN directly to the script,
the workaround is to use input files, supply the filenames on the
command line, and use Perl's magic ARGV operator "<>". Support for
redirections may be added in a future version.
The JavaScript shown below is not strictly necessary, it is also
possible to specify a "src='...'" attribute directly in the IFrame
tag, for example using the "Copy Frame URL" link shown in the editor.
Note that implementing an automatic resize of the IFrame to fit its
contents is nontrivial when sandboxing is enabled, which is why a
fixed height is used below. However, see for example
http://davidjbradshaw.github.io/iframe-resizer/ - examples of how
to use this are included in the source files here.
-->
<iframe id="perl1" sandbox="allow-scripts" class="perleditor" style="height:20em;"></iframe>
<script>
document.getElementById('perl1').src =
"perleditor.html#" + encodeURIComponent(JSON.stringify( {
inputs: [ { fn:"in.txt", text:"Foo\nBar\nQuz" } ],
cmdline: "perl -CSD -pe 's/[aeiou]/_/g' in.txt",
} ));
// Example of how to use the Optional "IFrame Resizer":
iFrameResize({checkOrigin:false}, document.getElementById('perl1'));
</script>
<p>This example includes several files:</p>
<!-- The following example demonstrates (almost) all of the possible
options that can be passed to the editor.
The "cmdline" option and the corresponding input box in the editor
only support very basic quoting constructs:
- Strings in double quotes may contain whitespace, \\, and/or \",
the latter two will be changed to \ and " respectively;
- strings in single quotes may contain whitespace, \\, and/or \',
the latter two will be changed to \ and ' respectively;
- other strings (without whitespace) will not be modified.
Note: As a consequence of these rules, inside of single or double
quotes, both \\n and \n resolve to \n (for any character "n" that
is not a backslash or single resp. double quote).
Instead of "cmdline", you may specify "argv" as an array ("cmdline"
overrides "argv"). This array should *not* include "perl" as the
first element; this is added automatically.
So that it can be displayed in the input box, the "argv" array
will be encoded into a single string - this means that if you want
full control over the formatting of the command line as it is
displayed to the user in the editor, use "cmdline" instead. The
"Copy JSON" data will include both "cmdline" and "argv" (so you
can choose to delete whichever one you don't need), while "Copy
URL" will include only "cmdline" (for brevity).
You may specify the text of a script via "script", or, alternatively,
a "script_url" from which the script is to be fetched - however, be
aware that cross-origin restrictions may limit your ability to fetch
URLs from other origins. You can specify the script's filename with
"script_fn".
Input files ("inputs") are specified as an array of objects; the
properties of the object are similar to the script: filenames are
specified with "fn", and the text of the file via "text", or
alternatively, you may specify a "url" from which the content is to
be fetched.
The output files ("outputs") are an array of filenames. After the
script finishes, the "Perl runner" will attempt to read these files
and display them to the user. It is also possible to specify output
files with the same name as an input file, for example if Perl's "-i"
option was used.
The current working directory of Perl defaults to the "home"
directory in Emscripten's virtual file system, currently
"/home/web_user", and all filenames are relative to this directory.
You may also specify absolute filenames such as "/tmp/foo.txt".
However, note that intermediate directories are currently not
automatically created, so if you specify files with nonexistent
directories like "/tmp/foo/bar.txt" or the relative "foo/bar.txt",
this will not work.
Additional options: Setting "mergeStdOutErr" to a true value causes
STDOUT and STDERR output to be output together, similar to the way
they would be on the console. *However,* note that WebPerl
currently doesn't think it's connected to a terminal, which means
that perl defaults to block instead of line buffering STDOUT, so
it may seem like you always see STDERR output before STDOUT. If you
want to truly intermix the two, turn on autoflush ("$|=1;").
If you set the "autorun" option, the editor will attempt to run the
script as soon as the runner is ready. *WARNING:* If you have
multiple editors embedded in the page, *do not* enable "autorun"
for more than one editor, as otherwise you will likely trigger a
race condition, resulting in an error being shown to the user.
-->
<iframe id="perl2" sandbox="allow-scripts" class="perleditor" style="height:42em;"></iframe>
<script>
document.getElementById('perl2').src =
"perleditor.html#" + encodeURIComponent(JSON.stringify( {
argv: ["devoweler.pl","mytext.txt","other.txt"],
script: "use warnings;\nuse strict;\n\nopen my $vfh, '>', 'vowels.txt' or die $!;\n"
+"while (<>) {\n\tprint $vfh $1 while s/([aeiou])/_/i;\n\tprint;\n}\nclose $vfh;",
script_fn: "devoweler.pl",
inputs: [
{ fn: "mytext.txt", text: "Foo\nBar\nQuz\n" },
{ fn: "other.txt", text: "Hello, World!" },
],
outputs: [ "vowels.txt" ],
autorun: true,
} ));
iFrameResize({checkOrigin:false}, document.getElementById('perl2'));
</script>
</body>
</html>

@ -0,0 +1,83 @@
body {
margin: 0.4em;
}
.text {
font-family: Calibri, Ubuntu, "Droid Sans", Tahoma, Arial, Helvetica, sans-serif;
font-size: 0.9em;
}
pre,textarea,code,.code,.filename,.CodeMirror {
font-family: Consolas, "Ubuntu Mono", "Droid Sans Mono", "Lucida Console", "Courier New", Courier, monospace;
}
pre {
margin: 0;
}
a {
text-decoration: none;
}
.CodeMirror {
border: 1px solid lightgrey;
height: auto;
}
.CodeMirror-scroll {
max-height: 12em;
}
.codewithfn {
margin-top: 0.4em;
}
.fnfuncs {
cursor: default;
}
.filename {
display: inline-block;
border: 0;
padding: 1px;
min-width: 1em;
cursor: auto;
}
.filefuncs {
display: inline-block;
padding-top: 2px;
position: absolute;
right: 0.2em;
}
.fakelink {
color: darkblue;
cursor: pointer;
}
.badfilename {
background-color: rgba(255,200,200,255);
/* also has a placeholder text */
min-width: 10em;
}
#perlctrl {
margin-top: 0.3em;
}
#misctools {
display: inline-block;
border: 1px solid grey;
padding: 1px 0.8em 1px 0.5em;
margin-top: 0.5em;
}
#runnerstate {
margin-top: 0.2em;
margin-bottom: 0.3em;
}
#runnererrors {
background-color: rgba(255,200,200,255);
margin-top: 0.3em;
margin-bottom: 0.3em;
padding: 0.1em 0.2em;
}
#inputhere, #outputhere {
text-align: right;
}
#footer {
margin-top: 0.5em;
}

@ -0,0 +1,552 @@
<!doctype html>
<html lang="en-us">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>WebPerl Perl Editor</title>
<!-- ##### WebPerl - http://webperl.zero-g.net #####
Copyright (c) 2018 Hauke Daempfling (haukex@zero-g.net)
at the Leibniz Institute of Freshwater Ecology and Inland Fisheries (IGB),
Berlin, Germany, http://www.igb-berlin.de
This program is free software; you can redistribute it and/or modify
it under the same terms as Perl 5 itself: either the GNU General Public
License as published by the Free Software Foundation (either version 1,
or, at your option, any later version), or the "Artistic License" which
comes with Perl 5.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the licenses for details.
You should have received a copy of the licenses along with this program.
If not, see http://perldoc.perl.org/index-licence.html
##### -->
<!-- Please see the documentation on how to use this in index.html. -->
<!--cacheable--><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css" integrity="sha256-oSrCnRYXvHG31SBifqP2PM1uje7SJUyX0nTwO2RJV54=" crossorigin="anonymous" />
<!--cacheable--><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.39.2/codemirror.min.css" integrity="sha256-I8NyGs4wjbMuBSUE40o55W6k6P7tu/7G28/JGUUYCIs=" crossorigin="anonymous" />
<link rel="stylesheet" href="perleditor.css" />
<!-- Optional "IFrame Resizer": -->
<!--cacheable--><script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/3.6.2/iframeResizer.contentWindow.min.js" integrity="sha256-dEPtZVO6cj6PAmBeDzFskohUob+woyzF6TaNcYpAk84=" crossorigin="anonymous"></script>
<!--cacheable--><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.39.2/codemirror.min.js" integrity="sha256-uRIJ6Wfou5cTtmcCvQNA9lvsYl8sUbZXxnfG+P79ssY=" crossorigin="anonymous"></script>
<!--cacheable--><script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.39.2/mode/perl/perl.min.js" integrity="sha256-Uu9QBfi8gU6J/MzQunal8ewmY+i/BbCkBrcAXA5bcCM=" crossorigin="anonymous"></script>
<!--cacheable--><script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script>
"use strict";
var mergeStdOutErr = false; // Possible To-Do for Later: could make an options hash
var perlRunner; // the Perl runner iframe found by findPerlRunner()
var buttonBlockers = {}; // for updateButtonState()
var lastExitStatus; // for runnerState()
var loadedRunnerIframe = false; // for findPerlRunner()
var autoRunPerl = false; // for the message listener
function makeCM (textarea,plain,ro) {
return CodeMirror.fromTextArea( textarea[0], {
viewportMargin: Infinity, // so browser's search works, not good for long documents though
lineNumbers:true, indentWithTabs:true,
tabSize:4, indentUnit:4,
mode: plain?"text/plain":"perl",
readOnly: ro?true:false,
} );
}
function runnerState (text) {
$('#runnerstate').text( text
/* not available until WebPerl v0.09-beta:
+ (lastExitStatus ? ' (last exit status was '+lastExitStatus+')'
: '') );
*/ );
}
function updateButtonState () {
$('#runperl').prop("disabled",
Object.keys(buttonBlockers).length>0 );
}
function stdOutput (which, data) { // which: 1=stdout, 2=stderr
if (mergeStdOutErr) which = 1;
var div = $(which==1?'#stdout':'#stderr');
div.show();
var cm = div.data('CodeMirrorInstance');
if (!cm) {
cm = makeCM($('textarea',div),1,1);
div.data('CodeMirrorInstance', cm);
}
if (data && data.length)
cm.setValue( cm.getValue() + data );
}
function clearStdOutput () {
$('#stdout,#stderr').each(function (i) {
var div = $(this);
var cm = div.data('CodeMirrorInstance');
if (cm) cm.setValue('');
div.hide();
});
}
function findPerlRunner () {
// assume calling this function means the runner isn't available
buttonBlockers.runnerState = 1;
updateButtonState();
// poll for perlRunner, which gets set by the message listener
var warnAt = Date.now() + 15*1000; // milliseconds
var loadIframe = Date.now() + 3*1000; // milliseconds
var pollId;
pollId = window.setInterval( function () {
if (perlRunner)
window.clearInterval(pollId);
else if (!loadedRunnerIframe && self===top && Date.now()>loadIframe) {
console.debug("Perl Editor is attempting to load Perl Runner...");
/* This is a special case: We appear to be the toplevel window,
* and are unable to contact the runner within a fixed amount of time.
* So we assume that someone has linked directly to this page instead
* of loading it in an IFrame, so we'll load the runner ourselves. */
$('<iframe/>',{name:"perlrunner",sandbox:"allow-scripts",
src:"perlrunner.html",style:"display:none;"})
.appendTo('body');
loadedRunnerIframe = true;
}
else {
if (window.parent && window.parent.frames["perlrunner"])
window.parent.frames["perlrunner"].postMessage(
{perlRunnerDiscovery:1}, '*');
if ( Date.now()>warnAt ) {
$('#runnererrors>pre').text("Perl does not appear to have loaded yet, still waiting...");
$('#runnererrors').show();
warnAt = Date.now() + 5*1000; // milliseconds
}
}
}, 100);
}
window.addEventListener('message', function (event) {
var data = event.data;
if (data["perlRunnerState"]) {
if ( data.perlRunnerState=="Ready" ) {
perlRunner = event.source;
delete buttonBlockers.runnerState;
updateButtonState();
if (autoRunPerl) {
autoRunPerl = false;
$('#runperl').click();
}
}
else if ( data.perlRunnerState=="Ended" ) {
if ('exitStatus' in data)
lastExitStatus = ''+data.exitStatus;
// we know the runner will reload itself now
perlRunner = null;
findPerlRunner();
}
runnerState("Perl is "+data.perlRunnerState);
}
else if (data["perlOutput"])
stdOutput(data.perlOutput.chan, data.perlOutput.data);
else if (data["perlOutputFiles"]) {
data.perlOutputFiles.forEach(function (outp) {
setupOutputFile(outp.fn, outp.text);
});
}
else if (data["perlRunnerError"]) {
$('#runnererrors').show();
$('#runnererrors>pre').append(data.perlRunnerError+"\n");
}
else if (data.substring(0,13)=="[iFrameSizer]") {} // ignore quietly
else console.warn("Perl Editor ignoring unknown message:",data);
});
function parseCmdLine(str) {
// not the prettiest code but it works
var re = /"((?:\\"|\\\\|[^"])*)"|'((?:\\'|\\\\|[^'])*)'|(\S+)/g;
var argv = [];
var match;
while ((match = re.exec(str)) !== null) {
if (typeof match[1] != 'undefined') argv.push(match[1].replace(/\\\\/g,"\\").replace(/\\"/g,"\""));
else if (typeof match[2] != 'undefined') argv.push(match[2].replace(/\\\\/g,'\\').replace(/\\'/g,'\''));
else if (typeof match[3] != 'undefined') argv.push(match[3]);
else throw "Unexpected match "+match;
}
return argv;
}
function encodeCmdLine(arr) {
var out = [];
for (var i=0; i<arr.length; i++) {
/* Note: we only *need* to encode strings if they contain /[\s'"\\]/,
* but since we want to show the users a command line similar to a shell,
* I've added $ */
out.push( arr[i].match(/[\s'"\\\$]/)
? "'"+arr[i].replace(/\\/g, "\\\\").replace(/'/g, "\\'")+"'"
: arr[i] );
}
return out.join(' ');
}
function fetchUrl(url,cm) { // fetch the contents of a URL into a CodeMirror instance
cm.setValue("Fetching URL\n"+url+"\nPlease wait...");
buttonBlockers["fetchUrls"]++;
updateButtonState();
$.get(url, function (data) {
cm.setValue(data);
},'text').fail(function (jqXHR,textStatus,errorThrown) {
cm.setValue("Fetching URL\n"+url+"\nFailed!\n"+textStatus+"\n"+errorThrown);
}).always(function () {
buttonBlockers.fetchUrls--;
if (!buttonBlockers.fetchUrls)
delete buttonBlockers.fetchUrls;
updateButtonState();
});
}
function makeCodeWithFn (fn,targ,ro,isscript) {
var div = $('<div/>',{class:"codewithfn"});
var fnfuncs = $('<div/>',{class:"fnfuncs"}).appendTo(div);
var filename = $('<input/>',{class:"filename code",type:"text",
placeholder:"Enter a filename!"})
.appendTo(fnfuncs);
filename.val(fn);
// autosize the filename text box via a hidden span
var fn_width = $('<span/>',
{class:"code",style:"display:none;white-space:pre;"}
).insertAfter(filename);
filename.on('input', function () {
var newfn = filename.val();
fn_width.text( newfn );
filename.width( fn_width.width()+10 );
if (newfn.length)
filename.removeClass("badfilename");
else
filename.addClass("badfilename");
});
/* we need to trigger this handler once when the input
* field is added to the document, we do this below */
var filefuncs = $('<div/>',{class:"filefuncs text"})
.appendTo(fnfuncs);
var conf = $('<span/>', {})
.append(
"&ensp;",
"Are you sure?",
"&ensp;",
$('<span/>',{class:"fakelink",text:"Yes"})
.click(function () {
div.remove();
if (isscript) $('#addscript').show();
}),
"&ensp;",
$('<span/>',{class:"fakelink",text:"Cancel"})
.click(function () { conf.hide(); }),
);
$('<span/>',{class:"fakelink",text:"Delete"})
.appendTo(filefuncs).click(function () {
conf.show();
});
conf.hide();
conf.appendTo(filefuncs);
var ta = $('<textarea/>').appendTo(div);
targ.before(div);
filename.trigger('input'); // see above
var cm = makeCM(ta, !(isscript||fn.match(/\.pl$/i)), ro);
div.data('CodeMirrorInstance', cm);
return {div:div,ta:ta,cm:cm};
}
function pickNewFilename (inNotOut) {
var x = inNotOut ? 'input' : 'output';
for (var i=1; i<1000; i++) {
var fn = x+i+".txt";
var found = $('div.'+x+'s .filename')
.filter(function(){ return $(this).val() == fn });
if (!found.length)
return fn;
}
$('#runnererrors>pre').text('Too many '+x+' files');
$('#runnererrors').show();
throw 'Too many '+x+' files';
}
function setupOutputFile (fn, text) {
var cm;
if (fn) {
var founddiv = $('div.outputs')
.filter(function(){ return $('.filename',this).val() == fn });
if (founddiv.length)
cm = founddiv.data('CodeMirrorInstance');
}
else
fn = pickNewFilename(false);
if (!cm) {
var cfn = makeCodeWithFn(fn, $('#outputhere'), 1);
cfn.div.addClass("outputs");
cm = cfn.cm;
}
cm.setValue( text ? text : '' );
}
function setupInputFile (inp) {
var fn = inp["fn"] ? inp.fn : pickNewFilename(true);
var cfn = makeCodeWithFn(fn, $('#inputhere'), 0);
cfn.div.addClass("inputs");
if (inp["text"])
cfn.cm.setValue(inp.text);
else if (inp["url"])
fetchUrl(inp.url, cfn.cm);
}
function getFileData () {
var filedata = {};
// command-line args
filedata.cmdline = $('#argv').val();
var argv = parseCmdLine( filedata.cmdline );
if ( argv.length<1 || argv[0]!="perl" ) {
$('#runnererrors>pre').text('Invalid command line, command must be "perl"');
$('#runnererrors').show();
return;
} // else
argv.shift();
$('#runnererrors>pre').text('');
$('#runnererrors').hide();
filedata.argv = argv;
// script
var scriptdiv = $('#script');
if (scriptdiv.is(':visible')) {
filedata.script = scriptdiv.data('CodeMirrorInstance').getValue();
filedata.script_fn = scriptdiv.find('.filename').val();
}
// inputs
$('.inputs').each(function () {
var div = $(this);
var fn = $('.filename',div).val();
var text = div.data('CodeMirrorInstance').getValue();
if (!filedata["inputs"]) filedata.inputs = [];
filedata.inputs.push( { fn:fn, text:text } );
});
// outputs
$('.outputs').each(function () {
var fn = $(this).find('.filename').val();
if (!filedata["outputs"]) filedata.outputs = [];
filedata.outputs.push(fn);
});
return filedata;
}
function copyit (what) {
var pageurl = $('#pageurl');
pageurl.val(what);
pageurl.show();
pageurl[0].select();
document.execCommand("copy");
pageurl.hide();
}
$(function () {
var hashdata = window.location.hash.substr(1);
var hash = hashdata.length>0 ? JSON.parse(decodeURIComponent(hashdata)) : {};
$('#showtools').click(function () {
$('#thetools,#footer').toggle();
$('#showtools').text( $('#thetools').is(':visible')
? 'Hide Tools' : 'Show Tools' );
});
$('#webperllink').click(function () {
$('#webperlurl').show();
});
// script
var addscript = $('#addscript');
if ( hash["script"] || hash["script_url"] ) {
var fn = hash["script_fn"] ? hash.script_fn : 'script.pl';
var cfn = makeCodeWithFn(fn, $('#perlctrl'), 0, 1);
cfn.div.attr("id", "script");
if (hash["script"])
cfn.cm.setValue(hash.script);
else if (hash["script_url"])
fetchUrl(hash.script_url, cfn.cm);
addscript.hide();
}
else
addscript.show();
$('#addscript .fakelink').click(function () {
addscript.hide();
if ($('#script').length) return;
var cfn = makeCodeWithFn('script.pl', $('#perlctrl'), 0, 1);
cfn.div.attr("id", "script");
cfn.cm.setValue("use warnings;\nuse strict;\n\n");
});
// command line
var argv_inp = $('#argv');
var argv_autosize = $('<span/>',
{class:"code",style:"display:none;white-space:pre;"}
).insertAfter(argv_inp);
argv_inp.on('input', function () {
argv_autosize.text( argv_inp.val() );
argv_inp.width( argv_autosize.width()+10 );
});
if (hash["cmdline"])
argv_inp.val(hash.cmdline);
else if (hash["argv"])
argv_inp.val("perl "+encodeCmdLine(hash.argv));
argv_inp.trigger('input');
// input files
$('.inputs').remove();
if ( hash["inputs"] ) hash.inputs.forEach(function(inp) {
setupInputFile(inp);
});
$('#addinput').click(function () {
setupInputFile( {} );
});
// stdout/stderr
var mergestdoe = $('#mergestdoe');
var stdout_fn = $('#stdout .filename');
if (hash["mergeStdOutErr"]) {
mergeStdOutErr = true;
stdout_fn.val("STDOUT+STDERR");
mergestdoe.text("Split STDOUT+ERR");
}
else {
mergeStdOutErr = false;
stdout_fn.val("STDOUT");
}
clearStdOutput();
mergestdoe.click(function () {
clearStdOutput();
mergeStdOutErr = !mergeStdOutErr;
stdout_fn.val( mergeStdOutErr ? "STDOUT+STDERR" : "STDOUT" );
mergestdoe.text( mergeStdOutErr ? "Split STDOUT+ERR" : "Merge STDOUT+ERR" );
});
// output files
$('.outputs').remove();
if ( hash["outputs"] ) hash.outputs.forEach(function(outp) {
setupOutputFile(outp);
});
$('#addoutput').click(function () {
setupOutputFile();
});
// autorun option
if (hash["autorun"])
autoRunPerl = true;
var autorunstate = $('#autorunstate');
$('#autoruntoggle').click(function () {
// the text keeps state (bit of a hack, I know)
autorunstate.text(
autorunstate.text().match(/without/i)
? "with" : "without" );
});
// "run perl" button
$('#runperl').click( function () {
clearStdOutput();
var rp_data = getFileData();
if (!rp_data) return;
delete rp_data.cmdline;
// send message to runner
buttonBlockers.runnerState = 1;
updateButtonState();
lastExitStatus = null;
runnerState("Requesting Perl Run...");
perlRunner.postMessage({ runPerl: rp_data }, '*');
});
// "copy url / json" function
$('#copyurl').click(function () {
var data = getFileData();
if (!data) return;
delete data.argv;
if (!autorunstate.text().match(/without/i)) data.autorun=true;
if (mergeStdOutErr) data.mergeStdOutErr=true;
var loc = new URL(window.location);
loc.hash = encodeURIComponent(JSON.stringify(data));
copyit(loc);
});
$('#copyjson').click(function () {
var data = getFileData();
if (!data) return;
if (!autorunstate.text().match(/without/i)) data.autorun=true;
if (mergeStdOutErr) data.mergeStdOutErr=true;
copyit(JSON.stringify(data, null, "\t"));
});
// start looking for the Perl runner
findPerlRunner();
});
</script>
</head>
<body>
<div id="inputhere" style="display:none;"></div>
<div id="perlctrl">
<button id="runperl"><code>perl</code> &#x25BA;</button>
<input type="text" id="argv" class="code" value='perl' />
</div>
<div id="runnerstate" class="text">
Loading...
</div>
<div id="runnererrors" style="display:none;">
<pre></pre>
</div>
<div id="stdout" class="codewithfn" style="display:none;">
<input type="text" class="filename code" readonly="readonly" value="STDOUT" size="14" />
<textarea></textarea>
</div>
<div id="stderr" class="codewithfn" style="display:none;">
<input type="text" class="filename code" readonly="readonly" value="STDERR" size="14" />
<textarea></textarea>
</div>
<div id="outputhere" style="display:none;"></div>
<div class="text">
<textarea id="pageurl" style="display:none;"></textarea>
<div id="misctools">
<span id="showtools" class="fakelink">Show Tools</span>
<span id="thetools" style="display:none;">
&nbsp;
<span id="addscript" style="display:none;">&bull;
<span class="fakelink">Add Script</span>
</span>
&bull;
<span id="addinput" class="fakelink">Add Input File</span>
&bull;
<span id="addoutput" class="fakelink">Add Output File</span>
&bull;
<span id="mergestdoe" class="fakelink">Merge STDOUT+ERR</span>
&bull;
<span id="copyurl" class="fakelink">Copy URL</span>
/ <span id="copyjson" class="fakelink">JSON</span>
(<span id="autorunstate">with</span>
<span id="autoruntoggle" class="fakelink">autorun</span>)
</span>
</div>
</div>
</div>
<div class="text" id="footer" style="display:none;">
Powered by <a href="http://webperl.zero-g.net/" target="_blank" id="webperllink">WebPerl</a> (beta)
<!-- Link with target="_blank" may not work in a sandboxed iframe, so provide URL: -->
<span id="webperlurl" style="display:none;">&nbsp; <code>http://webperl.zero-g.net/</code></span>
</div>
</body>
</html>

@ -0,0 +1,179 @@
<!doctype html>
<html lang="en-us">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>WebPerl Perl Runner</title>
<!-- ##### WebPerl - http://webperl.zero-g.net #####
Copyright (c) 2018 Hauke Daempfling (haukex@zero-g.net)
at the Leibniz Institute of Freshwater Ecology and Inland Fisheries (IGB),
Berlin, Germany, http://www.igb-berlin.de
This program is free software; you can redistribute it and/or modify
it under the same terms as Perl 5 itself: either the GNU General Public
License as published by the Free Software Foundation (either version 1,
or, at your option, any later version), or the "Artistic License" which
comes with Perl 5.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the licenses for details.
You should have received a copy of the licenses along with this program.
If not, see http://perldoc.perl.org/index-licence.html
##### -->
<!-- Please see the documentation on how to use this in index.html. -->
<!-- Possible To-Do for Later: This whole thing could probably also be
accomplished with a Web Worker, but that would probably require a
stripped-down version of webperl.js (that loads things without
using window.* and especially document.*
https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker
https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts
Of course, at some point I should investigate how difficult it really
is to re-start an Emscripten program...
-->
<!--script src="../webperl.js"></script-->
<script src="https://webperlcdn.zero-g.net/v0.07-beta/webperl.js"
integrity="sha256-jL8SB7St5ou4+hb0frK0k6VCQXsWQ1wolDrdU7i4juc=" crossorigin="anonymous"></script>
<script>
"use strict";
Perl.noMountIdbfs=true; // we're sandboxed
Perl.endAfterMain=true; // act like command-line perl
var knownClients = [];
var currentClient; // which client we're running Perl for, also keeps state
var curOutputFiles;
var stdbuf = [null,'',''];
function reportErr (err) {
if (currentClient)
currentClient.postMessage({ perlRunnerError: err },'*');
else
console.error(err);
}
Perl.addStateChangeListener(function (from,to) {
if (from==to) return; // won't be needed as of v0.09-beta
if (to=="Ended" && currentClient) {
for (var chan=1;chan<=2;chan++) // flush buffers
if (stdbuf[chan].length) {
currentClient.postMessage({ perlOutput: { chan:chan, data:stdbuf[chan] } },'*');
stdbuf[chan] = '';
}
currentClient.postMessage({ perlRunnerState: Perl.state,
exitStatus: Perl.exitStatus },'*');
for(var i=0; i<knownClients.length; i++)
if (knownClients[i]!=currentClient)
knownClients[i].postMessage({ perlRunnerState: Perl.state },'*');
if (curOutputFiles) {
var ofs = curOutputFiles.map(function (file) {
//TODO Later: Support binary files as well?
// {encoding:"binary"} => readFile returns Uint8Array
// Should then also provide the same support on FS.writeFile() as well
var of = { fn: file };
if (!file) return of;
try {
of.text = FS.readFile(file, {encoding:"utf8"});
}
catch (e) {
reportErr("couldn't read "+file+" because "+e);
}
return of;
});
currentClient.postMessage({ perlOutputFiles: ofs },'*');
}
}
else {
for(var i=0; i<knownClients.length; i++)
knownClients[i].postMessage({ perlRunnerState: Perl.state },'*');
}
if (to=="Ended") {
if (!currentClient)
console.error("Internal Error: Perl state change to Ended with no client");
window.location.reload(false);
}
});
Perl.output = function (str,chan) {
stdbuf[chan] += str;
var pos = stdbuf[chan].lastIndexOf("\n");
if (pos<0) return;
var lines = stdbuf[chan].slice(0,pos+1);
if (currentClient)
currentClient.postMessage({ perlOutput: { chan:chan, data:lines } },'*');
else
console.error("Internal Error: Output on",chan==1?"STDOUT":"STDERR","with no client:",lines);
stdbuf[chan] = stdbuf[chan].slice(pos+1);
};
function saveFile (fn, data) {
if (fn.substring(0,1)!='/') // if relative, make absolute
fn = FS.joinPath([FS.cwd(), fn]);
try {
FS.writeFile(fn, data);
}
catch (e) {
reportErr("couldn't write "+fn+" because "+e);
}
}
window.addEventListener('message', function (event) {
if (event.data["perlRunnerDiscovery"]) {
if (!knownClients.includes(event.source))
knownClients.push(event.source);
event.source.postMessage({ perlRunnerState: Perl.state },'*');
}
else if (event.data["runPerl"]) {
if (!knownClients.includes(event.source))
knownClients.push(event.source);
// check state
if (currentClient && currentClient !== event.source) {
console.error("Attempt to run Perl from",event.source,
"but am already running Perl for",currentClient);
reportErr("Attempt to run Perl (from "+event.origin
+") but am already running Perl for someone else (see JavaScript console)");
return;
} // else
currentClient = event.source;
if (Perl.state!="Ready") {
reportErr("Attempt to run Perl in state "+Perl.state);
return;
} // else
// set up files and run perl
var rp = event.data.runPerl;
//TODO: we don't check for overlaps in filenames between script+input files (maybe the editor should do that)
// one solution would be to just have the script be an input file (code mirror syntax highlighting based on filename?)
// note overlaps of output filenames with input files is ok
// we also don't check for duplicate filenames
if (rp["script"])
saveFile(rp["script_fn"] ? rp.script_fn : 'script.pl', rp.script);
//TODO Later: can we support STDIN? (probably need to look at webperl.js)
if (rp["inputs"])
rp.inputs.forEach(function (inp) {
if (!inp.fn) return;
saveFile(inp.fn, inp.text);
});
curOutputFiles = rp["outputs"];
Perl.start( rp["argv"] ? rp.argv : [] );
}
else console.warn("Perl Runner ignoring unknown message:", event.data);
});
Perl.init(function () {
Module['thisProgram'] = 'perl';
FS.currentPath = ENV.HOME; // NOTE: https://github.com/kripken/emscripten/issues/5873
});
</script>
</head>
<body>
</body>
</html>

@ -5,8 +5,8 @@
[Legal](legal.html) -
[GitHub Wiki](https://github.com/haukex/webperl/wiki) \]
🕸️🐪 Welcome to WebPerl!
======================
Welcome to WebPerl!
===================
WebPerl uses the power of [WebAssembly](https://webassembly.org/) and
@ -36,8 +36,10 @@ js('document')->getElementById('my_button')
- [**Download `webperl_prebuilt_v0.07-beta.zip`**](https://github.com/haukex/webperl/releases/download/v0.07-beta/webperl_prebuilt_v0.07-beta.zip)
- [**Get the sources on GitHub**](https://github.com/haukex/webperl)
For a web application written with WebPerl, see the
[**WebPerl Regex Tester** (beta)](regex.html)!
For web applications written with WebPerl, see:
- [**WebPerl Code Demo Editor** (beta)](democode/index.html)
- [**WebPerl Regex Tester** (beta)](regex.html)
Quick Start
@ -56,17 +58,17 @@ Quick Start
$ plackup webperl.psgi
HTTP::Server::PSGI: Accepting connections at http://0:5000/
- Then point your browser at
<http://localhost:5000/webperl_demo.html> or
<http://localhost:5000/mini_ide/webperl_mini_ide.html>,
and have a look at the HTML source of `webperl_demo.html`.
- Then point your browser at <http://localhost:5000/webperl_demo.html>
and have a look at its source. The ZIP archive also contains several
other examples, which you can access at <http://localhost:5000/>.
You may also host the contents of the above ZIP archive on a webserver of your choice,
as described in [Using WebPerl](using.html). (Note: In `webperl_demo.html`, you'll
likely see "AJAX Failed!", which is to be expected since your webserver won't
know how to handle the example AJAX request.)
You may also host the contents of the above ZIP archive on a webserver of your
choice, or some browsers will support opening the files locally; both are
described in [Serving WebPerl](using.html#serving-webperl).
(Note: In `webperl_demo.html`, you'll likely see "AJAX Failed!", which is to be
expected since your webserver won't know how to handle the example AJAX request.)
Have fun! ️🐪
Have fun!
***

@ -5,8 +5,8 @@
Legal -
[GitHub Wiki](https://github.com/haukex/webperl/wiki) \]
🕸️🐪 WebPerl Author, Copyright, and License
=========================================
WebPerl Author, Copyright, and License
======================================
**WebPerl - <http://webperl.zero-g.net>**

@ -5,8 +5,31 @@ Notes -
[Legal](legal.html) -
[GitHub Wiki](https://github.com/haukex/webperl/wiki) \]
🕸️🐪 Misc. Notes on WebPerl
=========================
Misc. Notes on WebPerl
======================
Links
-----
Other places I've written about WebPerl:
- [Run Perl 5 in the Browser!](https://www.perlmonks.org/?node_id=1220426)
- [WebPerl Regex Tester (beta)](https://www.perlmonks.org/?node_id=1221705)
- [Embedding WebPerl Code Demos into Other Pages](https://www.perlmonks.org/?node_id=1223812)
I also gave a [lightning talk](http://act.perlconference.org/tpc-2018-glasgow/talk/7475)
at YAPC::EU 2018 in Glasgow: [Video Link](https://youtu.be/KrGSg7uVZj0?t=29520)
[![DOI 10.5281/zenodo.1400490](https://zenodo.org/badge/DOI/10.5281/zenodo.1400490.svg)](https://doi.org/10.5281/zenodo.1400490)
### In the Press
- [Reddit: Your scientists were so preoccupied with whether or not they could, they didn't stop to think if they should (2018-10-22)](https://www.reddit.com/r/programmingcirclejerk/comments/9qerw5/your_scientists_were_so_preoccupied_with_whether/) 😉
- [Hacker News (2018-10-21)](https://news.ycombinator.com/item?id=18269071)
- [Reddit /r/programming (2018-10-21)](https://www.reddit.com/r/programming/comments/9q65tf/run_perl_in_the_browser_with_webperl/)
- [Facebook Group "Perl Programmers" (2018-10-20)](https://www.facebook.com/groups/perlprogrammers/permalink/2141844605848316/)
- [Reddit /r/perl: WebPerl Regex Tester (2018-09-05)](https://www.reddit.com/r/perl/comments/9d5n77/webperl_regex_tester/)
TODOs
@ -36,6 +59,13 @@ TODOs
- Add Perl.Util functions for making file uploads and downloads easier
- Plus an example showing how to use it to run a "legacy" Perl script with inputs and output
- Perhaps create a CPAN Bundle:: module or similar for `build.pl` deps?
- There is some potential for restructuring:
- `Perl.glue()` and `Perl.dispatch()` could go into `WebPerl.xs` (?)
- Parts of `webperl.js` could go into `common_preamble.js` or `WebPerl.xs`,
so that `emperl.js` is runnable on its own in a Web Worker (?)
(see notes in `perlrunner.html` / `e12f1aa25a000`)
- `nodeperl_dev_prerun.js` could probably be merged into that as well
- Regarding the funky syntax highlighting on GitHub: <https://github.com/atom/language-html/issues/88#issuecomment-431361414>
3. See Also
@ -45,6 +75,16 @@ TODOs
or using the included `findtodo.sh`.
SSL
---
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout selfsigned.key -out selfsigned.crt
...
Common Name (e.g. server FQDN or YOUR name) []: localhost
...
$ plackup --enable-ssl --ssl-key-file=selfsigned.key --ssl-cert-file=selfsigned.crt web/webperl.psgi
# then go to https://localhost:5000 and accept the certificate warning
Possible Improvements
---------------------
@ -94,12 +134,11 @@ Release Checklist
At a minimum there is:
- `web/webperl.js` - `Perl.WebPerlVersion`
- `emperl5/ext/WebPerl/WebPerl.pm` - `$VERSION`
- `pages/index.md` - download links
- `pages/index.md` and `pages/using.md` - download links
- Update [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) values as needed, e.g.:
$ perl -wMstrict -MDigest -le 'open my $fh, "<:raw", "web/webperl.js" or die $!;
print Digest->new("SHA-256")->addfile($fh)->b64digest'
$ perl -wMstrict -MDigest::SRI=sri -le 'print sri "SHA-256","web/webperl.js"'
- Build and create dist, e.g. `build/build.pl --reconfig --dist=webperl_prebuilt_v0.07-beta`

@ -5,8 +5,8 @@
[Legal](legal.html) -
[GitHub Wiki](https://github.com/haukex/webperl/wiki/Using-WebPerl) \]
🕸️🐪 Using WebPerl
================
Using WebPerl
=============
**Notice: WebPerl is very much in beta.**
@ -30,6 +30,19 @@ Your feedback is always appreciated!
Basic Usage
-----------
### Getting WebPerl
If you plan on building WebPerl, for example if you'd like to add more CPAN
modules, then head on over to [Building WebPerl](building.html). Otherwise, if
you'd just like to get started quickly and work with the prebuilt WebPerl
(includes many of the Perl core modules plus a couple extras), then download
[`webperl_prebuilt_v0.07-beta.zip`](https://github.com/haukex/webperl/releases/download/v0.07-beta/webperl_prebuilt_v0.07-beta.zip)
and unpack it. This ZIP file includes the contents of the
[`web`](https://github.com/haukex/webperl/tree/master/web) directory of the
source code, as well as the build products `emperl.*` (currently three files).
If you'd like to work with the source code as checked out from GitHub, then you
can copy these `emperl.*` files into the `web` directory of the source tree.
### Serving WebPerl
You should serve WebPerl via a webserver of your choice, or you can
@ -219,6 +232,8 @@ after having passed them to JavaScript, as that will probably cause mysterious b
Emscripten provides a virtual file system that also provides a few "fake" files such
as `/home/web_user`, `/dev`, and others, so that it resembles a normal *NIX file system.
This filesystem resides entirely in memory in the browser.
Perl's libraries (`*.pm`) are installed into this virtual file system at `/opt/perl`.
Note that because the `perl` binary is compiled to WebAssembly and XS libraries are
statically linked into it, you won't find any `perl` binary or shared library files in the
@ -368,7 +383,7 @@ This option was added in `v0.05-beta`.
Enable this option at any time to get additional trace-level output
to `console.debug()`. Defaults to `false`.
#### `Perl.addStateChangeListener`
#### `Perl.addStateChangeListener(function)`
Pass this function a `function (from,to) {...}` to register a new handler
for state changes of the Perl interpreter.
@ -377,7 +392,7 @@ The states currently are:
- `"Uninitialized"` - `Perl.init` has not been called yet.
- `"Initializing"` - `Perl.init` is currently operating.
- `"Ready" - `Perl.init` is finished and `Perl.start` can be called.
- `"Ready"` - `Perl.init` is finished and `Perl.start` can be called.
- `"Running"` - The Perl interpreter is running, `Perl.eval` and `Perl.end` may be called
- `"Ended"` - The Perl interpreter has ended.
~~You might receive several state change notifications for this state.~~
@ -479,9 +494,11 @@ stored in a Perl scalar `$foo` pointing to a JavaScript object `foo`:
regular Perl data structure (deep copy). Note that JavaScript functions are
kept wrapped inside anonymous Perl `sub`s.
- `jscode` returns a string of JavaScript code that represents a reference
to the JavaScript object. You should treat the string as an opaque value,
no guarantees are made about its format and whether it may change in future
releases. *Do not* call JavaScript's `delete` on this value.
to the JavaScript object. **Warning:** Treat this value as read-only in
JavaScript! *Do not* assign to this value, call JavaScript's `delete` on
this value, etc. (calling methods that may mutate the object is ok, though).
You should treat the string as an opaque value, no guarantees are made about
its format and whether it may change in future releases.
This is an advanced function that should not normally be needed,
unless you are building strings of JavaScript to run. In that case, you
may need to wrap the value in parentheses for it to evaluate correctly in
@ -504,19 +521,20 @@ Unlike the JavaScript to Perl mappings, values are (currently¹) generally *copi
Perl to JavaScript, instead of being *referenced*.
The exceptions are Perl `sub`s and `WebPerl::JSObject`s.
- Perl arrayrefs become JavaScript arrays
- Perl hashrefs become JavaScript objects
- Perl arrayrefs become JavaScript arrays (deep copy)
- Perl hashrefs become JavaScript objects (deep copy)
- Perl coderefs become JavaScript functions -
**Warning:** please see the discussion in
["Memory Management and Anonymous `sub`s"](#memory-management-and-anonymous-subs)!
- Perl `WebPerl::JSObject`s become references to the wrapped JavaScript objects
(i.e. the underlying JS object is passed back to JS transparently)
- Perl numbers/strings are copied to JavaScript via `Cpanel::JSON::XS::encode_json`
(with its `allow_nonref` option enabled). This means that the choice
for whether to encode a Perl scalar as a JavaScript number or string is
left up to the module, and is subject to the usual ambiguities when
serializing Perl scalars. See
[the `Cpanel::JSON::XS` documentation](https://metacpan.org/pod/Cpanel::JSON::XS).
- Other references, including objects, are currently not supported.
- Other references, including Perl objects, are currently not supported.
¹ So far, the focus of WebPerl has been to replace JavaScript with Perl, and
therefore on accessing JavaScript from Perl, and not as much the other

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Loading…
Cancel
Save