Copied "web/democode" from 'master'

Only changes were to get webperl.js from CDN and rename "demo.html" to
"index.html".
gh-pages
Hauke D 7 years ago
parent 774a56066a
commit 7c48cfdd28

@ -0,0 +1,166 @@
<!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 solid black;
width: 100%;
max-width: 50em;
margin: 0.2em 0;
}
</style>
</head>
<body>
<p>
This page demonstrates the embeddable
<strong><a href="http://webperl.zero-g.net" target="_blank">&#x1F578;&#xFE0F;&#x1F42A; WebPerl</a>
Code Demo Editor</strong>, 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>
<!-- 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.
-->
<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",
} ));
</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" supports only very basic quoting constructs.
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.
-->
<iframe id="perl2" sandbox="allow-scripts" class="perleditor" style="height:42em;"></iframe>
<script>
document.getElementById('perl2').src =
"perleditor.html#" + encodeURIComponent(JSON.stringify( {
cmdline: "perl 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" ],
} ));
</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,470 @@
<!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
##### -->
<!-- CACHED FROM "https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css" --><link rel="stylesheet" href="../_cache/normalize.min.css" integrity="sha256-oSrCnRYXvHG31SBifqP2PM1uje7SJUyX0nTwO2RJV54=" crossorigin="anonymous" />
<!-- CACHED FROM "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.39.2/codemirror.min.css" --><link rel="stylesheet" href="../_cache/codemirror.min.css" integrity="sha256-I8NyGs4wjbMuBSUE40o55W6k6P7tu/7G28/JGUUYCIs=" crossorigin="anonymous" />
<link rel="stylesheet" href="perleditor.css" />
<!-- CACHED FROM "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.39.2/codemirror.min.js" --><script src="../_cache/codemirror.min.js" integrity="sha256-uRIJ6Wfou5cTtmcCvQNA9lvsYl8sUbZXxnfG+P79ssY=" crossorigin="anonymous"></script>
<!-- CACHED FROM "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.39.2/mode/perl/perl.min.js" --><script src="../_cache/perl.min.js" integrity="sha256-Uu9QBfi8gU6J/MzQunal8ewmY+i/BbCkBrcAXA5bcCM=" crossorigin="anonymous"></script>
<!-- CACHED FROM "https://code.jquery.com/jquery-3.3.1.min.js" --><script src="../_cache/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script>
"use strict";
var mergeStdOutErr = 0; // 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()
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
+ (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();
}
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 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]);
}
return argv;
}
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 = {};
// 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 () {
var hashdata = window.location.hash.substr(1);
var hash = hashdata.length>0 ? JSON.parse(decodeURIComponent(hashdata)) : {};
// 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.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);
argv_inp.trigger('input');
// input files
$('.inputs').remove();
if ( hash["inputs"] ) hash.inputs.forEach(function(inp) {
setupInputFile(inp);
});
$('#addinput').click(function () {
setupInputFile( {} );
});
// stdout/stderr
if (hash["mergeStdOutErr"]) {
mergeStdOutErr = 1;
$('#stdout .filename').val("STDOUT+STDERR");
}
else {
mergeStdOutErr = 0;
$('#stdout .filename').val("STDOUT");
}
clearStdOutput();
// output files
$('.outputs').remove();
if ( hash["outputs"] ) hash.outputs.forEach(function(outp) {
setupOutputFile(outp);
});
$('#addoutput').click(function () {
setupOutputFile();
});
// "run perl" button
$('#runperl').click( function () {
clearStdOutput();
// command-line args
var argv = parseCmdLine(argv_inp.val());
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();
var rp_data = getFileData();
rp_data.argv = argv;
// send message to runner
buttonBlockers.runnerState = 1;
updateButtonState();
lastExitStatus = null;
runnerState("Requesting Perl Run...");
perlRunner.postMessage({ runPerl: rp_data }, '*');
});
// "copy frame url" function
$('#copyurl').click(function () {
var pageurl = $('#pageurl');
var data = getFileData();
data.cmdline = $('#argv').val();
var loc = new URL(window.location);
loc.hash = encodeURIComponent(JSON.stringify(data));
pageurl.val(loc);
pageurl.show();
pageurl[0].select();
document.execCommand("copy");
pageurl.hide();
});
// 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">
Tools
&nbsp;
<span id="addscript" class="fakelink" style="display:none;">Add Script</span>
&nbsp;
<span id="addinput" class="fakelink">Add Input File</span>
&nbsp;
<span id="addoutput" class="fakelink">Add Output File</span>
&nbsp;
<span id="copyurl" class="fakelink">Copy Frame URL</span>
</div>
</div>
</div>
<div class="text" id="footer">
Powered by <a href="http://webperl.zero-g.net/" target="_blank">&#x1F578;&#xFE0F;&#x1F42A; WebPerl</a> (beta)
<!-- Link with target="_blank" may not work in a sandboxed iframe, so provide URL: -->
&nbsp; <code>http://webperl.zero-g.net/</code>
</div>
</body>
</html>

@ -0,0 +1,177 @@
<!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
##### -->
<!-- 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>
Loading…
Cancel
Save