mirror of
https://github.com/inexorabletash/jsbasic.git
synced 2025-06-21 12:24:00 +00:00
dos2unix; factor out index.js; use addEvent/getClassList shims; change screen style using CSS classes
This commit is contained in:
420
index.htm
420
index.htm
@ -5,14 +5,14 @@
|
|||||||
<title>Applesoft BASIC in JavaScript</title>
|
<title>Applesoft BASIC in JavaScript</title>
|
||||||
<link rel="Stylesheet" href="styles.css" type="text/css">
|
<link rel="Stylesheet" href="styles.css" type="text/css">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- Suppress browser compat button -->
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- Suppress browser compat button -->
|
||||||
<script type="text/javascript" src="../polyfill/polyfill.js?update=2011-12-17"></script>
|
<script type="text/javascript" src="../polyfill/polyfill.js?update=2012-02-08"></script>
|
||||||
<script type="text/javascript" src="../polyfill/harmony.js?update=2011-12-17"></script>
|
<script type="text/javascript" src="../polyfill/harmony.js?update=2011-12-17"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function () {
|
(function () {
|
||||||
function load(url) { document.write('<script type="text/javascript" src="' + url + '"></' + 'script>'); }
|
function load(url) { document.write('<script type="text/javascript" src="' + url + '"></' + 'script>'); }
|
||||||
if (!window.JSON) { load('../polyfill/json2.js'); }
|
if (!window.JSON) { load('../polyfill/json2.js'); }
|
||||||
if (!window.localStorage || !window.sessionStorage) { load('../polyfill/storage.js'); }
|
if (!window.localStorage || !window.sessionStorage) { load('../polyfill/storage.js'); }
|
||||||
if (!('getContext' in document.createElement('canvas'))) { load('../polyfill/flashcanvas.js'); }
|
if (!('getContext' in document.createElement('canvas'))) { load('../polyfill/flashcanvas.js'); }
|
||||||
}());
|
}());
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript" src="../polyfill/keyboard.js"></script>
|
<script type="text/javascript" src="../polyfill/keyboard.js"></script>
|
||||||
@ -37,11 +37,11 @@ By <a href="mailto:inexorabletash@hotmail.com">Joshua Bell</a>
|
|||||||
|
|
||||||
<!-- Screen -->
|
<!-- Screen -->
|
||||||
<div id="frame" class="frame" style="float: left; margin: 10px;">
|
<div id="frame" class="frame" style="float: left; margin: 10px;">
|
||||||
<div id="screen-wrapper" class="wrapper">
|
<div id="screen-wrapper" class="wrapper">
|
||||||
<div id="lores" class="lores"></div>
|
<div id="lores" class="lores"></div>
|
||||||
<canvas id="hires" width="560" height="384" class="hires"></canvas>
|
<canvas id="hires" width="560" height="384" class="hires"></canvas>
|
||||||
<canvas id="hires2" width="560" height="384" class="hires"></canvas>
|
<canvas id="hires2" width="560" height="384" class="hires"></canvas>
|
||||||
<div id="screen" class="tty"></div>
|
<div id="screen" class="tty"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -56,69 +56,69 @@ By <a href="mailto:inexorabletash@hotmail.com">Joshua Bell</a>
|
|||||||
<input type="button" value="Stop" id="btn_stop" disabled="disabled">
|
<input type="button" value="Stop" id="btn_stop" disabled="disabled">
|
||||||
|
|
||||||
<select id="lb_files">
|
<select id="lb_files">
|
||||||
<option disabled selected="selected">Select a file...</option>
|
<option disabled selected="selected">Select a file...</option>
|
||||||
<option value="sample.basic">DEMOS</option>
|
<option value="sample.basic">DEMOS</option>
|
||||||
|
|
||||||
<option disabled>---- Tests ----</option>
|
<option disabled>---- Tests ----</option>
|
||||||
<option value="sample.unittests">Unit Tests</option>
|
<option value="sample.unittests">Unit Tests</option>
|
||||||
<option value="sample.keyboard">Keyboard Test</option>
|
<option value="sample.keyboard">Keyboard Test</option>
|
||||||
<option value="sample.charset">Charset Test</option>
|
<option value="sample.charset">Charset Test</option>
|
||||||
|
|
||||||
|
|
||||||
<option disabled>---- User Submissions ----</option>
|
<option disabled>---- User Submissions ----</option>
|
||||||
|
|
||||||
<option disabled>Games</option>
|
<option disabled>Games</option>
|
||||||
<option value="simple.pong">SIMPLE.PONG</option>
|
<option value="simple.pong">SIMPLE.PONG</option>
|
||||||
<option value="sample.adventure">Text Adventure (Floyd McWilliams)</option>
|
<option value="sample.adventure">Text Adventure (Floyd McWilliams)</option>
|
||||||
<option value="sample.pacman">(Not Really) ASCII Pac-Man (Michael Kemp)</option>
|
<option value="sample.pacman">(Not Really) ASCII Pac-Man (Michael Kemp)</option>
|
||||||
<option value="sample.puzzler">Puzzler (Gregg Buntin)</option>
|
<option value="sample.puzzler">Puzzler (Gregg Buntin)</option>
|
||||||
<option value="sample.hangman">Hangman (Mike Gleason)</option>
|
<option value="sample.hangman">Hangman (Mike Gleason)</option>
|
||||||
<option value="sample.raindrops">Catch the Raindrop (Nicholas Merchant)</option>
|
<option value="sample.raindrops">Catch the Raindrop (Nicholas Merchant)</option>
|
||||||
<option value="sample.jot">JOT (Mike Gleason)</option>
|
<option value="sample.jot">JOT (Mike Gleason)</option>
|
||||||
|
|
||||||
<option disabled>Graphics</option>
|
<option disabled>Graphics</option>
|
||||||
<option value="sample.rodscolorpattern">Rod's Color Pattern</option>
|
<option value="sample.rodscolorpattern">Rod's Color Pattern</option>
|
||||||
<option value="sample.hacker">Hacker Logo (markwstock)</option>
|
<option value="sample.hacker">Hacker Logo (markwstock)</option>
|
||||||
<option value="sample.loreswalk">Random LoRes (John Melesky)</option>
|
<option value="sample.loreswalk">Random LoRes (John Melesky)</option>
|
||||||
<option value="sample.hireswalk">Random HiRes (John Melesky)</option>
|
<option value="sample.hireswalk">Random HiRes (John Melesky)</option>
|
||||||
<option value="sample.sierpinski">Sierpinski Triangles (Kevin Miller)</option>
|
<option value="sample.sierpinski">Sierpinski Triangles (Kevin Miller)</option>
|
||||||
<option value="sample.stringart">String Art (Chris Heric)</option>
|
<option value="sample.stringart">String Art (Chris Heric)</option>
|
||||||
<option value="sample.paint">Drawing Program (Brian Broker)</option>
|
<option value="sample.paint">Drawing Program (Brian Broker)</option>
|
||||||
<option value="sample.scribble">Scribble (William Simms)</option>
|
<option value="sample.scribble">Scribble (William Simms)</option>
|
||||||
<option value="sample.connections">Connections (Gregg Buntin)</option>
|
<option value="sample.connections">Connections (Gregg Buntin)</option>
|
||||||
<option value="sample.squiggle">Squiggle (Gregg Buntin)</option>
|
<option value="sample.squiggle">Squiggle (Gregg Buntin)</option>
|
||||||
<option value="sample.boys_surface">Boy's Surface (Lukas Innig)</option>
|
<option value="sample.boys_surface">Boy's Surface (Lukas Innig)</option>
|
||||||
<option value="sample.gaussian">Gaussian Distribution 2D (John Russ)</option>
|
<option value="sample.gaussian">Gaussian Distribution 2D (John Russ)</option>
|
||||||
<option value="sample.bitmaps">Bitmap Images (Brian Broker)</option>
|
<option value="sample.bitmaps">Bitmap Images (Brian Broker)</option>
|
||||||
<option value="sample.mandelbrot">Mandelbrot Set (c/o Gregory Lewis)</option>
|
<option value="sample.mandelbrot">Mandelbrot Set (c/o Gregory Lewis)</option>
|
||||||
<option value="sample.mandelbrot2">Mandelbrot Set in Color</option>
|
<option value="sample.mandelbrot2">Mandelbrot Set in Color</option>
|
||||||
<option value="sample.steve">Steve (Nicola Foggi)</option>
|
<option value="sample.steve">Steve (Nicola Foggi)</option>
|
||||||
|
|
||||||
<option disabled>Other</option>
|
<option disabled>Other</option>
|
||||||
<option value="sample.primes">Prime Sieve (Kevin Miller)</option>
|
<option value="sample.primes">Prime Sieve (Kevin Miller)</option>
|
||||||
<option value="sample.february">February Surprise (Antti Pirskanen)</option>
|
<option value="sample.february">February Surprise (Antti Pirskanen)</option>
|
||||||
<option value="sample.hellosine">Hello World Sine Wave (Jamie Beu)</option>
|
<option value="sample.hellosine">Hello World Sine Wave (Jamie Beu)</option>
|
||||||
<option value="sample.bodymass">Body Mass Index Calculator (Tim Dwyer)</option>
|
<option value="sample.bodymass">Body Mass Index Calculator (Tim Dwyer)</option>
|
||||||
|
|
||||||
<option disabled>---- Traveller RPG Utilities ----</option>
|
<option disabled>---- Traveller RPG Utilities ----</option>
|
||||||
<option value="TRADER C">TRADER</option>
|
<option value="TRADER C">TRADER</option>
|
||||||
<option value="sample.sectorgen">Traveller Sector Generator</option>
|
<option value="sample.sectorgen">Traveller Sector Generator</option>
|
||||||
<option value="sample.zhorelay">Zhodani Relay Station Placement</option>
|
<option value="sample.zhorelay">Zhodani Relay Station Placement</option>
|
||||||
<option value="sample.readsector">Read Sector File</option>
|
<option value="sample.readsector">Read Sector File</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<input type="button" id="btn_capture" value="Echo to "Printer"" title="Pops up a "printer" window and echoes all output there, so you can copy/paste">
|
<input type="button" id="btn_capture" value="Echo to "Printer"" title="Pops up a "printer" window and echoes all output there, so you can copy/paste">
|
||||||
|
|
||||||
<!-- Source code editor inserted here -->
|
<!-- Source code editor inserted here -->
|
||||||
<div id="editorframe"></div>
|
<div id="editorframe"></div>
|
||||||
|
|
||||||
<form id="submission" method="post" enctype="text/plain" action="mailto:inexorabletash@hotmail.com?subject=Applesoft%20Sample%20Submission">
|
<form id="submission" method="post" enctype="text/plain" action="mailto:inexorabletash@hotmail.com?subject=Applesoft%20Sample%20Submission">
|
||||||
<textarea name="source" id="source" style="display: none;">
|
<textarea name="source" id="source" style="display: none;">
|
||||||
</textarea>
|
</textarea>
|
||||||
<input type="submit" id="btn_share" value="Share your sample!">
|
<input type="submit" id="btn_share" value="Share your sample!">
|
||||||
<input type="button" id="btn_save" value="Save Program" title="Save the current program. NOTE: Can only save one program.">
|
<input type="button" id="btn_save" value="Save Program" title="Save the current program. NOTE: Can only save one program.">
|
||||||
<input type="button" id="btn_load" value="Load Program" title="Load a previously saved program. WARNING: The current program will be lost!">
|
<input type="button" id="btn_load" value="Load Program" title="Load a previously saved program. WARNING: The current program will be lost!">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -126,27 +126,27 @@ By <a href="mailto:inexorabletash@hotmail.com">Joshua Bell</a>
|
|||||||
|
|
||||||
<h3 id="notes">Notes & Known Issues</h3>
|
<h3 id="notes">Notes & Known Issues</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>The BASIC program is compiled to JavaScript before execution. Syntax errors are therefore detected at compile-time
|
<li>The BASIC program is compiled to JavaScript before execution. Syntax errors are therefore detected at compile-time
|
||||||
rather than at run-time as on a traditional interpreter. For example, the following program would run without errors
|
rather than at run-time as on a traditional interpreter. For example, the following program would run without errors
|
||||||
on an Apple since the erroneous second statement is never reached. <code>10 END : CHR$(PRINT)</code>
|
on an Apple since the erroneous second statement is never reached. <code>10 END : CHR$(PRINT)</code>
|
||||||
<li>Handling of BASIC code that does not match the canonical <code>LIST</code> output format may not behave as on an Apple:
|
<li>Handling of BASIC code that does not match the canonical <code>LIST</code> output format may not behave as on an Apple:
|
||||||
<ul>
|
|
||||||
<li>Keyword parsing differs from Applesoft command line. For example <code>FOR I = S TO P</code> doesn't collapse into <code>FOR I = STOP</code>.
|
|
||||||
<li>The interpreter doesn't actually care about line numbers for statement ordering (just for <code>GOTO</code>/<code>GOSUB</code> targets and <code>IF</code> statements). So <code>20 PRINT "A"</code>, <code>10 PRINT "B"</code> will just print A, then B
|
|
||||||
</ul>
|
|
||||||
<li>To improve readability, lines may start with <code>:</code> and continue the previously numbered line.
|
|
||||||
<li>Floating point overflow is only detected on variable assignment.
|
|
||||||
<li>The DOS operating system implements only a subset of DOS 3.3 and ProDOS useful for basic file I/O.
|
|
||||||
<li>Except for a small number of compatibility shims for common operations (e.g. keyboard strobe), commands that refer to assembly routines (<code>PEEK</code>, <code>POKE</code>, <code>CALL</code>, <code>USR</code> etc.), shape tables, or tape I/O are not implemented.
|
|
||||||
<li>Commands that operate on the program itself (<code>LIST</code>, <code>RUN</code>, <code>DEL</code>, etc.) are not implemented.
|
|
||||||
<li>You can run your basic programs from the command line (with only basic text input and output, and no graphics or DOS commands):
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>On Windows, download <a href="basic.js">basic.js</a> and run from a command prompt via:
|
<li>Keyword parsing differs from Applesoft command line. For example <code>FOR I = S TO P</code> doesn't collapse into <code>FOR I = STOP</code>.
|
||||||
<code>cscript.exe basic.js your_basic_program.txt</code>
|
<li>The interpreter doesn't actually care about line numbers for statement ordering (just for <code>GOTO</code>/<code>GOSUB</code> targets and <code>IF</code> statements). So <code>20 PRINT "A"</code>, <code>10 PRINT "B"</code> will just print A, then B
|
||||||
<li>On Mac/Linux, install <a href="http://www.mozilla.org/rhino/">Mozilla Rhino</a>,
|
|
||||||
download <a href="basic.js">basic.js</a> and run from the command prompt via:
|
|
||||||
<code>java -jar PATH_TO/js.jar basic.js your_program.txt</code>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
<li>To improve readability, lines may start with <code>:</code> and continue the previously numbered line.
|
||||||
|
<li>Floating point overflow is only detected on variable assignment.
|
||||||
|
<li>The DOS operating system implements only a subset of DOS 3.3 and ProDOS useful for basic file I/O.
|
||||||
|
<li>Except for a small number of compatibility shims for common operations (e.g. keyboard strobe), commands that refer to assembly routines (<code>PEEK</code>, <code>POKE</code>, <code>CALL</code>, <code>USR</code> etc.), shape tables, or tape I/O are not implemented.
|
||||||
|
<li>Commands that operate on the program itself (<code>LIST</code>, <code>RUN</code>, <code>DEL</code>, etc.) are not implemented.
|
||||||
|
<li>You can run your basic programs from the command line (with only basic text input and output, and no graphics or DOS commands):
|
||||||
|
<ul>
|
||||||
|
<li>On Windows, download <a href="basic.js">basic.js</a> and run from a command prompt via:
|
||||||
|
<code>cscript.exe basic.js your_basic_program.txt</code>
|
||||||
|
<li>On Mac/Linux, install <a href="http://www.mozilla.org/rhino/">Mozilla Rhino</a>,
|
||||||
|
download <a href="basic.js">basic.js</a> and run from the command prompt via:
|
||||||
|
<code>java -jar PATH_TO/js.jar basic.js your_program.txt</code>
|
||||||
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3 id="todo">To Do</h3>
|
<h3 id="todo">To Do</h3>
|
||||||
@ -176,7 +176,6 @@ By <a href="mailto:inexorabletash@hotmail.com">Joshua Bell</a>
|
|||||||
.CodeMirror-scroll { height: 100%; }
|
.CodeMirror-scroll { height: 100%; }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript" src="basic.js?2012-02-08"></script>
|
<script type="text/javascript" src="basic.js?2012-02-08"></script>
|
||||||
<script type="text/javascript" src="bell.js"></script>
|
<script type="text/javascript" src="bell.js"></script>
|
||||||
<script type="text/javascript" src="tty.js"></script>
|
<script type="text/javascript" src="tty.js"></script>
|
||||||
@ -185,266 +184,17 @@ By <a href="mailto:inexorabletash@hotmail.com">Joshua Bell</a>
|
|||||||
<script type="text/javascript" src="dos.js"></script>
|
<script type="text/javascript" src="dos.js"></script>
|
||||||
<script type="text/javascript" src="printer.js"></script>
|
<script type="text/javascript" src="printer.js"></script>
|
||||||
<script type="text/javascript" src="feed.js"></script>
|
<script type="text/javascript" src="feed.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript" src="index.js"></script>
|
||||||
|
|
||||||
// not quite ready for jQuery...
|
|
||||||
var $ = function (id) { return document.getElementById(id); };
|
|
||||||
|
|
||||||
//
|
|
||||||
// Main
|
|
||||||
//
|
|
||||||
window.onload = function() {
|
|
||||||
|
|
||||||
$('lb_files').selectedIndex = 0;
|
|
||||||
|
|
||||||
var bell = (function() {
|
|
||||||
var b = new Bell(/^.*\/|/.exec(window.location)[0]);
|
|
||||||
return function() { b.play(); };
|
|
||||||
} ());
|
|
||||||
|
|
||||||
|
|
||||||
var tty = new TTY($('screen'), $('keyboard'), bell);
|
|
||||||
var dos = new DOS(tty);
|
|
||||||
|
|
||||||
var lores = new LoRes($('lores'), 40, 48);
|
|
||||||
var hires = new HiRes($('hires'), 280, 192);
|
|
||||||
var hires2 = new HiRes($('hires2'), 280, 192);
|
|
||||||
var display = {
|
|
||||||
state: { graphics: false, full: true, page1: true, lores: true },
|
|
||||||
setState: function(state, value /* ... */) {
|
|
||||||
var args = Array.prototype.slice.call(arguments);
|
|
||||||
while (args.length) {
|
|
||||||
state = args.shift();
|
|
||||||
value = args.shift();
|
|
||||||
this.state[state] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.graphics) {
|
|
||||||
lores.show(this.state.lores);
|
|
||||||
hires.show(!this.state.lores && this.state.page1);
|
|
||||||
hires2.show(!this.state.lores && !this.state.page1);
|
|
||||||
tty.splitScreen(tty.getScreenSize().height - (this.state.full ? 0 : 4));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lores.show(false);
|
|
||||||
hires.show(false);
|
|
||||||
hires2.show(false);
|
|
||||||
tty.splitScreen(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var pdl = [0, 0, 0, 0];
|
|
||||||
|
|
||||||
// Lexical highlighting
|
|
||||||
var editor = new CodeMirror($('editorframe'), {
|
|
||||||
mode: 'basic',
|
|
||||||
tabMode: 'default',
|
|
||||||
content: $('source').value,
|
|
||||||
height: '100%'
|
|
||||||
});
|
|
||||||
|
|
||||||
$('btn_share').onclick = function() {
|
|
||||||
// Load up the hidden text area with the current source
|
|
||||||
$('source').value = getSource();
|
|
||||||
};
|
|
||||||
|
|
||||||
function getSource() {
|
|
||||||
return editor.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setSource(source) {
|
|
||||||
editor.setValue(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not let certain events take focus away from "keyboard"
|
|
||||||
function keyboardFocus(e) {
|
|
||||||
e = e ? e : window.event;
|
|
||||||
|
|
||||||
tty.focus();
|
|
||||||
e.cancelBubble = true; // IE
|
|
||||||
e.returnValue = false;
|
|
||||||
if (e.stopPropagation) { e.stopPropagation(); } // W3C
|
|
||||||
if (e.preventDefault) { e.preventDefault(); } // e.g. to block arrows from scrolling the page
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$('lores').onclick = keyboardFocus;
|
|
||||||
$('hires').onclick = keyboardFocus;
|
|
||||||
$('hires2').onclick = keyboardFocus;
|
|
||||||
$('screen').onclick = keyboardFocus;
|
|
||||||
$('frame').onclick = keyboardFocus;
|
|
||||||
$('frame').onblur = keyboardFocus; // Needed on IE, not sure why it tries to get focus
|
|
||||||
|
|
||||||
$('keyboard').onfocus = function() { $('frame').style.backgroundColor = '#204020'; };
|
|
||||||
$('keyboard').onblur = function() { $('frame').style.backgroundColor = ''; };
|
|
||||||
|
|
||||||
var program;
|
|
||||||
$('btn_run').onclick = function() {
|
|
||||||
dos.reset();
|
|
||||||
tty.reset();
|
|
||||||
tty.autoScroll = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
program = basic.compile(getSource());
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
if (e instanceof basic.ParseError) {
|
|
||||||
editor.setCursor({ line: e.line - 1, ch: e.column - 1 });
|
|
||||||
console.log(e.message +
|
|
||||||
' (source line:' + e.line + ', column:' + e.column + ')');
|
|
||||||
}
|
|
||||||
alert(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stopped = false;
|
|
||||||
updateUI();
|
|
||||||
|
|
||||||
program.init({
|
|
||||||
tty: tty,
|
|
||||||
hires: hires,
|
|
||||||
hires2: hires2,
|
|
||||||
lores: lores,
|
|
||||||
display: display,
|
|
||||||
paddle: function(n) { return pdl[n]; }
|
|
||||||
});
|
|
||||||
setTimeout(driver, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
$('btn_stop').onclick = function() {
|
|
||||||
tty.reset(); // cancel any blocking input
|
|
||||||
stopped = true;
|
|
||||||
updateUI();
|
|
||||||
};
|
|
||||||
|
|
||||||
$('lb_files').onchange = function() {
|
|
||||||
var sel = $('lb_files');
|
|
||||||
loadFile('samples/' + sel.value + ".txt", setSource);
|
|
||||||
};
|
|
||||||
|
|
||||||
$('btn_save').onclick = function() { window.localStorage.setItem("save_program", getSource()); };
|
|
||||||
$('btn_load').onclick = function() { setSource(window.localStorage.getItem("save_program")); };
|
|
||||||
|
|
||||||
// Add a "printer" on demand
|
|
||||||
$('btn_capture').onclick = function() {
|
|
||||||
|
|
||||||
$('btn_capture').disabled = true;
|
|
||||||
var printer = new Printer(tty);
|
|
||||||
|
|
||||||
printer.onclose = function() {
|
|
||||||
$('btn_capture').disabled = false;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Mouse-as-Joystick
|
|
||||||
var wrapper = $('screen-wrapper');
|
|
||||||
wrapper.onmousemove = function(e) {
|
|
||||||
e = e || window.event;
|
|
||||||
|
|
||||||
// compute relative coordinates
|
|
||||||
var x = e.clientX, y = e.clientY, elem = wrapper;
|
|
||||||
while (elem) {
|
|
||||||
x -= elem.offsetLeft - elem.scrollLeft;
|
|
||||||
y -= elem.offsetTop - elem.scrollTop;
|
|
||||||
elem = elem.offsetParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
function clamp(n, min, max) { return Math.min(Math.max(n, min), max); }
|
|
||||||
pdl[0] = clamp(x / (wrapper.offsetWidth - 1), 0, 1);
|
|
||||||
pdl[1] = clamp(y / (wrapper.offsetHeight - 1), 0, 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var stopped = true;
|
|
||||||
function updateUI() {
|
|
||||||
$("btn_stop").disabled = stopped ? "disabled" : "";
|
|
||||||
$("btn_run").disabled = stopped ? "" : "disabled";
|
|
||||||
$("lb_files").disabled = stopped ? "" : "disabled";
|
|
||||||
|
|
||||||
if (stopped) {
|
|
||||||
$("btn_run").focus();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
tty.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Expose a RESPONSIVE |---+--------| FAST slider in the UI
|
|
||||||
// Number of steps to execute before yielding execution
|
|
||||||
// (Use a prime to minimize risk of phasing with loops)
|
|
||||||
var NUM_SYNCHRONOUS_STEPS = 37;
|
|
||||||
|
|
||||||
function driver() {
|
|
||||||
var state = basic.STATE_RUNNING;
|
|
||||||
var statements = NUM_SYNCHRONOUS_STEPS;
|
|
||||||
|
|
||||||
while (!stopped && state === basic.STATE_RUNNING && statements > 0) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
state = program.step(driver);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
alert(e.message ? e.message : e);
|
|
||||||
stopped = true;
|
|
||||||
updateUI();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
statements -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state === basic.STATE_STOPPED || stopped) {
|
|
||||||
stopped = true;
|
|
||||||
updateUI();
|
|
||||||
}
|
|
||||||
else if (state === basic.STATE_BLOCKED) {
|
|
||||||
// Fall out
|
|
||||||
}
|
|
||||||
else // state === basic.STATE_RUNNING
|
|
||||||
{
|
|
||||||
setTimeout(driver, 0); // Keep going
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function loadFile(filename, callback) {
|
|
||||||
var req = new XMLHttpRequest();
|
|
||||||
var url = encodeURI(filename); // not encodeURIComponent, so paths can be specified
|
|
||||||
var async = true;
|
|
||||||
req.open("GET", url, async);
|
|
||||||
req.onreadystatechange = function() {
|
|
||||||
if (req.readyState === XMLHttpRequest.DONE) {
|
|
||||||
if (req.status === 200 || req.status === 0) // 0 for file:// protocol
|
|
||||||
{
|
|
||||||
callback(req.responseText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
req.send(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// load default
|
|
||||||
loadFile('samples/sample.default.txt', setSource);
|
|
||||||
|
|
||||||
// Show change history
|
|
||||||
atomToHtml('feed.xml?' + Math.random(), $('feed'));
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var _gaq = _gaq || [];
|
var _gaq = _gaq || [];
|
||||||
_gaq.push(['_setAccount', 'UA-18610679-3']);
|
_gaq.push(['_setAccount', 'UA-18610679-3']);
|
||||||
_gaq.push(['_trackPageview']);
|
_gaq.push(['_trackPageview']);
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
236
index.js
Normal file
236
index.js
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
|
||||||
|
window.onload = function () {
|
||||||
|
|
||||||
|
var $ = function (s) { return document.querySelector(s); };
|
||||||
|
|
||||||
|
$('#lb_files').selectedIndex = 0;
|
||||||
|
|
||||||
|
var bell = (function () {
|
||||||
|
var b = new Bell(/^.*\/|/.exec(window.location)[0]);
|
||||||
|
return function () { b.play(); };
|
||||||
|
} ());
|
||||||
|
|
||||||
|
|
||||||
|
var tty = new TTY($('#screen'), $('#keyboard'), bell);
|
||||||
|
var dos = new DOS(tty);
|
||||||
|
|
||||||
|
var lores = new LoRes($('#lores'), 40, 48);
|
||||||
|
var hires = new HiRes($('#hires'), 280, 192);
|
||||||
|
var hires2 = new HiRes($('#hires2'), 280, 192);
|
||||||
|
var display = {
|
||||||
|
state: { graphics: false, full: true, page1: true, lores: true },
|
||||||
|
setState: function (state, value /* ... */) {
|
||||||
|
var args = Array.prototype.slice.call(arguments);
|
||||||
|
while (args.length) {
|
||||||
|
state = args.shift();
|
||||||
|
value = args.shift();
|
||||||
|
this.state[state] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.graphics) {
|
||||||
|
lores.show(this.state.lores);
|
||||||
|
hires.show(!this.state.lores && this.state.page1);
|
||||||
|
hires2.show(!this.state.lores && !this.state.page1);
|
||||||
|
tty.splitScreen(tty.getScreenSize().height - (this.state.full ? 0 : 4));
|
||||||
|
} else {
|
||||||
|
lores.show(false);
|
||||||
|
hires.show(false);
|
||||||
|
hires2.show(false);
|
||||||
|
tty.splitScreen(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var pdl = [0, 0, 0, 0];
|
||||||
|
|
||||||
|
// Lexical highlighting
|
||||||
|
var editor = new CodeMirror($('#editorframe'), {
|
||||||
|
mode: 'basic',
|
||||||
|
tabMode: 'default',
|
||||||
|
content: $('#source').value,
|
||||||
|
height: '100%'
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#btn_share').onclick = function () {
|
||||||
|
// Load up the hidden text area with the current source
|
||||||
|
$('#source').value = getSource();
|
||||||
|
};
|
||||||
|
|
||||||
|
function getSource() {
|
||||||
|
return editor.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSource(source) {
|
||||||
|
editor.setValue(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not let certain events take focus away from "keyboard"
|
||||||
|
function keyboardFocus(e) {
|
||||||
|
tty.focus();
|
||||||
|
e.stopPropagation(); // W3C
|
||||||
|
e.preventDefault(); // e.g. to block arrows from scrolling the page
|
||||||
|
}
|
||||||
|
|
||||||
|
addEvent($('#lores'), 'click', keyboardFocus);
|
||||||
|
addEvent($('#hires'), 'click', keyboardFocus);
|
||||||
|
addEvent($('#hires2'), 'click', keyboardFocus);
|
||||||
|
addEvent($('#screen'), 'click', keyboardFocus);
|
||||||
|
addEvent($('#frame'), 'click', keyboardFocus);
|
||||||
|
addEvent($('#frame'), 'blur', keyboardFocus); // Needed on IE, not sure why it tries to get focus
|
||||||
|
|
||||||
|
addEvent($('#keyboard'), 'focus', function () {
|
||||||
|
getClassList($('#frame')).add('focused');
|
||||||
|
});
|
||||||
|
addEvent($('#keyboard'), 'blur', function () {
|
||||||
|
getClassList($('#frame')).remove('focused');
|
||||||
|
});
|
||||||
|
|
||||||
|
var program;
|
||||||
|
addEvent($('#btn_run'), 'click', function () {
|
||||||
|
dos.reset();
|
||||||
|
tty.reset();
|
||||||
|
tty.autoScroll = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
program = basic.compile(getSource());
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof basic.ParseError) {
|
||||||
|
editor.setCursor({ line: e.line - 1, ch: e.column - 1 });
|
||||||
|
console.log(e.message +
|
||||||
|
' (source line:' + e.line + ', column:' + e.column + ')');
|
||||||
|
}
|
||||||
|
alert(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopped = false;
|
||||||
|
updateUI();
|
||||||
|
|
||||||
|
program.init({
|
||||||
|
tty: tty,
|
||||||
|
hires: hires,
|
||||||
|
hires2: hires2,
|
||||||
|
lores: lores,
|
||||||
|
display: display,
|
||||||
|
paddle: function (n) { return pdl[n]; }
|
||||||
|
});
|
||||||
|
setTimeout(driver, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
addEvent($('#btn_stop'), 'click', function () {
|
||||||
|
tty.reset(); // cancel any blocking input
|
||||||
|
stopped = true;
|
||||||
|
updateUI();
|
||||||
|
});
|
||||||
|
|
||||||
|
addEvent($('#lb_files'), 'change', function () {
|
||||||
|
var sel = $('#lb_files');
|
||||||
|
loadFile('samples/' + sel.value + ".txt", setSource);
|
||||||
|
});
|
||||||
|
|
||||||
|
addEvent($('#btn_save'), 'click', function () {
|
||||||
|
window.localStorage.setItem("save_program", getSource());
|
||||||
|
});
|
||||||
|
addEvent($('#btn_load'), 'click', function () {
|
||||||
|
setSource(window.localStorage.getItem("save_program"));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a "printer" on demand
|
||||||
|
addEvent($('#btn_capture'), 'click', function () {
|
||||||
|
$('#btn_capture').disabled = true;
|
||||||
|
var printer = new Printer(tty);
|
||||||
|
|
||||||
|
printer.onclose = function () {
|
||||||
|
$('#btn_capture').disabled = false;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Mouse-as-Joystick
|
||||||
|
var wrapper = $('#screen-wrapper');
|
||||||
|
addEvent(wrapper, 'mousemove', function (e) {
|
||||||
|
// compute relative coordinates
|
||||||
|
var x = e.clientX, y = e.clientY, elem = wrapper;
|
||||||
|
while (elem) {
|
||||||
|
x -= elem.offsetLeft - elem.scrollLeft;
|
||||||
|
y -= elem.offsetTop - elem.scrollTop;
|
||||||
|
elem = elem.offsetParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clamp(n, min, max) { return Math.min(Math.max(n, min), max); }
|
||||||
|
pdl[0] = clamp(x / (wrapper.offsetWidth - 1), 0, 1);
|
||||||
|
pdl[1] = clamp(y / (wrapper.offsetHeight - 1), 0, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var stopped = true;
|
||||||
|
function updateUI() {
|
||||||
|
$("#btn_stop").disabled = stopped ? "disabled" : "";
|
||||||
|
$("#btn_run").disabled = stopped ? "" : "disabled";
|
||||||
|
$("#lb_files").disabled = stopped ? "" : "disabled";
|
||||||
|
|
||||||
|
if (stopped) {
|
||||||
|
$("#btn_run").focus();
|
||||||
|
} else {
|
||||||
|
tty.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Expose a RESPONSIVE |---+--------| FAST slider in the UI
|
||||||
|
// Number of steps to execute before yielding execution
|
||||||
|
// (Use a prime to minimize risk of phasing with loops)
|
||||||
|
var NUM_SYNCHRONOUS_STEPS = 37;
|
||||||
|
|
||||||
|
function driver() {
|
||||||
|
var state = basic.STATE_RUNNING;
|
||||||
|
var statements = NUM_SYNCHRONOUS_STEPS;
|
||||||
|
|
||||||
|
while (!stopped && state === basic.STATE_RUNNING && statements > 0) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
state = program.step(driver);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
alert(e.message ? e.message : e);
|
||||||
|
stopped = true;
|
||||||
|
updateUI();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
statements -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state === basic.STATE_STOPPED || stopped) {
|
||||||
|
stopped = true;
|
||||||
|
updateUI();
|
||||||
|
} else if (state === basic.STATE_BLOCKED) {
|
||||||
|
// Fall out
|
||||||
|
} else { // state === basic.STATE_RUNNING
|
||||||
|
setTimeout(driver, 0); // Keep going
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function loadFile(filename, callback) {
|
||||||
|
var req = new XMLHttpRequest();
|
||||||
|
var url = encodeURI(filename); // not encodeURIComponent, so paths can be specified
|
||||||
|
var async = true;
|
||||||
|
req.open("GET", url, async);
|
||||||
|
req.onreadystatechange = function () {
|
||||||
|
if (req.readyState === XMLHttpRequest.DONE) {
|
||||||
|
if (req.status === 200 || req.status === 0) {
|
||||||
|
callback(req.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
req.send(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load default
|
||||||
|
loadFile('samples/sample.default.txt', setSource);
|
||||||
|
|
||||||
|
// Show change history
|
||||||
|
atomToHtml('feed.xml?' + Math.random(), $('#feed'));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
@ -6,11 +6,13 @@ td { vertical-align: top; }
|
|||||||
|
|
||||||
/* Apple II Screen */
|
/* Apple II Screen */
|
||||||
.frame {width: 560px; height: 384px; border-style: ridge; border-width: 10px; border-color: gray; padding: 10px; background-color: #202020; }
|
.frame {width: 560px; height: 384px; border-style: ridge; border-width: 10px; border-color: gray; padding: 10px; background-color: #202020; }
|
||||||
|
.frame.focused { background-color: #204020; }
|
||||||
.wrapper { width: 560px; height: 384px; overflow: hidden; padding: 0; margin: 0; position: relative; }
|
.wrapper { width: 560px; height: 384px; overflow: hidden; padding: 0; margin: 0; position: relative; }
|
||||||
.lores {position: absolute; z-index: 2; left: 0; top: 0; width: 560px; height: 384px; overflow: hidden; visibility: hidden; }
|
.lores {position: absolute; z-index: 2; left: 0; top: 0; width: 560px; height: 384px; overflow: hidden; visibility: hidden; }
|
||||||
.hires {position: absolute; z-index: 2; left: 0; top: 0; width: 560px; height: 384px; overflow: hidden; visibility: hidden; background-color: #000000; }
|
.hires {position: absolute; z-index: 2; left: 0; top: 0; width: 560px; height: 384px; overflow: hidden; visibility: hidden; background-color: #000000; }
|
||||||
.tty {position: absolute; z-index: 3; left: 0; top: 0; width: 560px; height: 384px; overflow: hidden; }
|
.tty {position: absolute; z-index: 3; left: 0; top: 0; width: 560px; height: 384px; overflow: hidden; }
|
||||||
|
|
||||||
|
|
||||||
/* Character cells */
|
/* Character cells */
|
||||||
.normal { background-color: #000000; color: #80ff80; }
|
.normal { background-color: #000000; color: #80ff80; }
|
||||||
.inverse { background-color: #80ff80; color: #000000; }
|
.inverse { background-color: #80ff80; color: #000000; }
|
||||||
|
Reference in New Issue
Block a user