
Update: Everything said in this post can be done better using XMLHttpRequest object, as explained in Running Gacela programs in the browser (rectification). If you want to know how not to do it, read on 😀
Gacela project, which I have worked nearly two years and a half, consists of three subprojects:
- Gacela, language definition and the compiler / interpreter, that runs locally on the computer.
- Lisp2js, the Lisp to Javascript compiler for translating Gacela programs and running them on a web page.
- Gacela on Wheels, a web environment for game development using Gacela.
Last months I’m focused in Lisp2js and in the ability to run programs written with Gacela in the browser. The idea is to include a Javascript code as file2js(‘mi_game.lisp’); init_game(); in a web page, translating Lisp code into Javascript code, embedding it into the page and running it. But I encountered a difficulty that is not expected and that surprised me; Javascript, for security reasons, has no functions for working with files.
Next, I’ll explain how we can load files using Javascript and how can Gacela add Javascript code to a web page and run it. Another day I’ll explain Lisp2js‘ inner workings.
To access a file with Javascript (I am talking of files at the same domain) we’ll use the iframe element. There are other ways, for example with Ajax, but that requires using some PHP on the server and the idea is to run locally without a web server.
Here is my code:
<html>
<head>
<script type="text/javascript" src="lisp2js.js"></script>
<script id="head_js" type="text/javascript"></script>
<script type="text/javascript">
function load_file (my_file, func) {
var el = document.getElementById('my_iframe');
if (el == null) {
var el = document.createElement("iframe");
el.setAttribute('id', 'my_iframe');
el.onload = function() { run_my_code(func); }
el.style.display='none';
document.body.appendChild(el);
}
el.setAttribute('src', my_file);
}
function run_my_code (func) {
var el = document.getElementById('my_iframe');
var lisp_code = el.contentWindow.document.body.textContent || el.contentWindow.document.body.innerText;
var js_code = string2js(lisp_code);
document.getElementById('head_js').text = js_code;
func();
}
</script>
</head>
<body onLoad="load_file('my_program.lisp', function() { init(); });">
</body>
</html>
Two functions are used, the first to create an iframe and specify the file to open and the second to run our code. Two functions are needed because iframe works asynchronously, so we use onload property to say what to do at the end of the content loading.
The first function, load_file, creates the iframe, but if it already exists the function only indicates the file to load. Although in this example it does not make much sense, it’s usefull if we had to upload multiple files.
The second function, run_my_code, executes when the iframe just uploaded the file. By textContent or innerText properties (it depends on the browser) we collect the Lisp code, translate it to Javascript and embed it into the right section in the web page header. Last, we init the execution of the generated code.
Thus, although more transparent, Gacela is capable of loading Lisp code on a web page and run it. That is, the same code runs in the same way locally on a computer or remotely through the browser, which is what is intended.