Animate paper showing/hiding; add a few more compat shims; fix docs for CALL 54915

This commit is contained in:
Joshua Bell 2017-08-15 10:10:30 -07:00
parent d03e3590d8
commit 05b7bbca87
4 changed files with 104 additions and 114 deletions

View File

@ -489,6 +489,15 @@ this.basic = (function() {
}, },
0xFC9C: function() { // Clear from cursor to right 0xFC9C: function() { // Clear from cursor to right
if (env.tty.clearEOL) { env.tty.clearEOL(); } if (env.tty.clearEOL) { env.tty.clearEOL(); }
},
0xFD0C: function() { // Wait for key press
throw new BlockingInput(env.tty.readChar, function(_){});
},
0xFE84: function() { // Normal
if (env.tty.setTextStyle) { env.tty.setTextStyle(env.tty.TEXT_STYLE_NORMAL); }
},
0xFE80: function() { // Inverse
if (env.tty.setTextStyle) { env.tty.setTextStyle(env.tty.TEXT_STYLE_INVERSE); }
} }
}; };
@ -731,20 +740,15 @@ this.basic = (function() {
}, },
'get': function GET(lvalue) { 'get': function GET(lvalue) {
var im = env.tty.readChar, throw new BlockingInput(env.tty.readChar, function(entry) { lvalue(entry); });
ih = function(entry) {
lvalue(entry);
};
throw new BlockingInput(im, ih);
}, },
'input': function INPUT(prompt /* , ...varlist */) { 'input': function INPUT(prompt /* , ...varlist */) {
var varlist = Array.prototype.slice.call(arguments, 1); // copy for closure var varlist = Array.prototype.slice.call(arguments, 1); // copy for closure
var im, ih; var im = function(cb) { return env.tty.readLine(cb, prompt); };
im = function(cb) { return env.tty.readLine(cb, prompt); }; var ih = function(entry) {
ih = function(entry) {
var parts = [], var parts = [],
stream = new Stream(entry); stream = new Stream(entry);
parseDataInput(stream, parts); parseDataInput(stream, parts);
@ -753,7 +757,7 @@ this.basic = (function() {
varlist.shift()(parts.shift()); varlist.shift()(parts.shift());
} catch (e) { } catch (e) {
if (e instanceof basic.RuntimeError && if (e instanceof basic.RuntimeError &&
e.code === ERRORS.TYPE_MISMATCH[0]) { e.code === ERRORS.TYPE_MISMATCH[0]) {
e.code = ERRORS.REENTER[0]; e.code = ERRORS.REENTER[0];
e.message = ERRORS.REENTER[1]; e.message = ERRORS.REENTER[1];
} }
@ -964,7 +968,7 @@ this.basic = (function() {
if (!hires) { runtime_error('Hires graphics not supported'); } if (!hires) { runtime_error('Hires graphics not supported'); }
var coords = Array.prototype.slice.call(arguments), var coords = Array.prototype.slice.call(arguments),
size = hires.getScreenSize(), x, y; size = hires.getScreenSize(), x, y;
while (coords.length) { while (coords.length) {
x = coords.shift() >> 0; x = coords.shift() >> 0;
@ -1181,8 +1185,8 @@ this.basic = (function() {
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
var match, test, endOfStatement, endOfProgram, var match, test, endOfStatement, endOfProgram,
currLine = 0, currColumn = 0, currLine = 0, currColumn = 0,
currLineNumber = 0; currLineNumber = 0;
function parse_error(msg) { function parse_error(msg) {
return new basic.ParseError(msg + " in line " + currLineNumber, return new basic.ParseError(msg + " in line " + currLineNumber,
@ -1253,7 +1257,7 @@ this.basic = (function() {
// data - DATA blah,"blah",blah // data - DATA blah,"blah",blah
var start = true, var start = true,
stream = new Stream(source); stream = new Stream(source);
function nextToken() { function nextToken() {
var token = {}, newline = start, ws; var token = {}, newline = start, ws;
@ -1375,25 +1379,9 @@ this.basic = (function() {
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
function quote(string) { function quote(string) {
// From json2.js (http://www.json.org/js.html) return JSON.stringify(string);
var escapable = /[\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"': '\\"',
'\\': '\\\\'
};
return '"' + string.replace(escapable, function(a) {
var c = meta[a];
return c ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"';
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// //
// Recursive Descent Parser // Recursive Descent Parser
@ -1453,7 +1441,7 @@ this.basic = (function() {
function parsePValue() { function parsePValue() {
var name = match('identifier'), var name = match('identifier'),
subscripts = parseSubscripts(); subscripts = parseSubscripts();
if (subscripts) { if (subscripts) {
identifiers.arrays[name] = true; identifiers.arrays[name] = true;
@ -1476,8 +1464,8 @@ this.basic = (function() {
function parseUserfunction() { function parseUserfunction() {
var name = match('identifier'), var name = match('identifier'),
type = vartype(name) === 'string' ? 'string' : 'number', type = vartype(name) === 'string' ? 'string' : 'number',
expr; expr;
// FUTURE: Allow differing argument type and return type // FUTURE: Allow differing argument type and return type
// (may require runtime type checks) // (may require runtime type checks)
@ -1499,10 +1487,10 @@ this.basic = (function() {
match("operator", "("); match("operator", "(");
var func = funlib[name], var func = funlib[name],
funcdesc = func.signature.slice(), funcdesc = func.signature.slice(),
rtype = funcdesc.shift(), rtype = funcdesc.shift(),
args = [], args = [],
atype; atype;
while (funcdesc.length) { while (funcdesc.length) {
atype = funcdesc.shift(); atype = funcdesc.shift();
@ -1543,8 +1531,8 @@ this.basic = (function() {
return parsefunction(match('reserved')); return parsefunction(match('reserved'));
} else if (test('identifier')) { } else if (test('identifier')) {
var name = match('identifier'), var name = match('identifier'),
type = vartype(name) === 'string' ? 'string' : 'number', type = vartype(name) === 'string' ? 'string' : 'number',
subscripts = parseSubscripts(); subscripts = parseSubscripts();
if (subscripts) { if (subscripts) {
identifiers.arrays[name] = true; identifiers.arrays[name] = true;
return { source: 'state.arrays[' + quote(name) + '].get([' + subscripts + '])', type: type }; return { source: 'state.arrays[' + quote(name) + '].get([' + subscripts + '])', type: type };
@ -1709,7 +1697,7 @@ this.basic = (function() {
} }
var keyword = test('identifier') ? kws.LET : match('reserved'), var keyword = test('identifier') ? kws.LET : match('reserved'),
name, type, subscripts, is_to, expr, param, args, prompt, trailing, js; name, type, subscripts, is_to, expr, param, args, prompt, trailing, js;
switch (keyword) { switch (keyword) {
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View File

@ -149,9 +149,9 @@ By <a href="mailto:inexorabletash@gmail.com">Joshua Bell</a>
<li><a target="_blank" href="http://www.6502asm.com/">6502asm.com</a> - a 6502 assembler/emulator in JavaScript <li><a target="_blank" href="http://www.6502asm.com/">6502asm.com</a> - a 6502 assembler/emulator in JavaScript
<li><a target="_blank" href="http://www.quitebasic.com/">Quite BASIC</a> - a similar project aimed at teaching programming <li><a target="_blank" href="http://www.quitebasic.com/">Quite BASIC</a> - a similar project aimed at teaching programming
<li><a target="_blank" href="http://navahogunleg.net/blog/my-projects/ng-basic/">NG-BASIC for Javascript</a> Navaho Gunleg's interpreter
</ul> </ul>
<div id="paper-spacer"></div>
<div id="paper"></div> <div id="paper"></div>

View File

@ -38,7 +38,7 @@ This is intended as a quick reference for the <a href="./">Applesoft BASIC in Ja
<li><a href="http://www.landsnail.com/a2ref.htm">Apple II Programmer's Reference: Applesoft Commands</a> <li><a href="http://www.landsnail.com/a2ref.htm">Apple II Programmer's Reference: Applesoft Commands</a>
<li><a href="http://www.apple2.org/faq/FAQ.applesoft.html">Applesoft BASIC Frequently Asked Questions (FAQ)</a> <li><a href="http://www.apple2.org/faq/FAQ.applesoft.html">Applesoft BASIC Frequently Asked Questions (FAQ)</a>
<li><a href="http://www.lazilong.com/apple_II/bbros/">Beagle Bros. Peeks, Pokes & Pointers Chart (Colors, ASCII values, etc)</a> <li><a href="http://www.lazilong.com/apple_II/bbros/">Beagle Bros. Peeks, Pokes & Pointers Chart (Colors, ASCII values, etc)</a>
<li><a href="http://beagle.applearchives.com/posters.htm">More Beagle Bros. Posters - including Apple Commands Chart, in PDF format</a> <li><a href="http://beagle.applearchives.com/posters.htm">More Beagle Bros. Posters &mdash; including Apple Commands Chart, in PDF format</a>
<li><a href="http://www.scribd.com/doc/232832/Applesoft-Basic-Programming-Reference-Manual">Applesoft Basic Programming Reference Manual</a> <li><a href="http://www.scribd.com/doc/232832/Applesoft-Basic-Programming-Reference-Manual">Applesoft Basic Programming Reference Manual</a>
</ul> </ul>
</section> </section>
@ -186,48 +186,51 @@ can be literals (unquoted strings), strings, or numbers
<section id="shims"> <section id="shims">
<h3>Compatibility Shims</h3> <h3>Compatibility Shims</h3>
<dl> <dl>
<dt>SPEED= <var>aexpr</var><dd>Set character output delay - <em>has no effect</em> <dt>SPEED= <var>aexpr</var><dd>Set character output delay &mdash; <em>has no effect</em>
<dt>POKE <var>aexpr</var>, <var>aexpr</var><dd>Set memory location to value <dt>POKE <var>aexpr</var>, <var>aexpr</var><dd>Set memory location to value
<ul> <ul>
<li><code>POKE 32,<var>n</var></code> - Text window left edge <li><code>POKE 32,<var>n</var></code> &mdash; Text window left edge
<li><code>POKE 33,<var>n</var></code> - Text window width <li><code>POKE 33,<var>n</var></code> &mdash; Text window width
<li><code>POKE 34,<var>n</var></code> - Text window top edge <li><code>POKE 34,<var>n</var></code> &mdash; Text window top edge
<li><code>POKE 35,<var>n</var></code> - Text window bottom <li><code>POKE 35,<var>n</var></code> &mdash; Text window bottom
<li><code>POKE 36,<var>n</var></code> - Text cursor x <li><code>POKE 36,<var>n</var></code> &mdash; Text cursor x
<li><code>POKE 37,<var>n</var></code> - Text cursor y <li><code>POKE 37,<var>n</var></code> &mdash; Text cursor y
<li><code>POKE 216,<var>n</var></code> - ONERR flag (<var>n</var> &lt; 128 disables ONERR handler) <li><code>POKE 216,<var>n</var></code> &mdash; ONERR flag (<var>n</var> &lt; 128 disables ONERR handler)
<li><code>POKE 230,<var>n</var></code> - Hi-Res plotting page (32 = page 1, 64 = page 2) <li><code>POKE 230,<var>n</var></code> &mdash; Hi-Res plotting page (32 = page 1, 64 = page 2)
<li><code>POKE 49168,0</code> - clear keyboard strobe <li><code>POKE 49168,0</code> &mdash; clear keyboard strobe
<li><code>POKE 49200,0</code> - toggle speaker (no-op) <li><code>POKE 49200,0</code> &mdash; toggle speaker (no-op)
<li><code>POKE 49232,0</code> - graphics mode <li><code>POKE 49232,0</code> &mdash; graphics mode
<li><code>POKE 49233,0</code> - text mode <li><code>POKE 49233,0</code> &mdash; text mode
<li><code>POKE 49234,0</code> - full graphics mode <li><code>POKE 49234,0</code> &mdash; full graphics mode
<li><code>POKE 49235,0</code> - mixed text/graphics mode <li><code>POKE 49235,0</code> &mdash; mixed text/graphics mode
<li><code>POKE 49236,0</code> - display page 1 <li><code>POKE 49236,0</code> &mdash; display page 1
<li><code>POKE 49237,0</code> - display page 2 <li><code>POKE 49237,0</code> &mdash; display page 2
<li><code>POKE 49238,0</code> - lores graphics mode <li><code>POKE 49238,0</code> &mdash; lores graphics mode
<li><code>POKE 49239,0</code> - hires graphics mode <li><code>POKE 49239,0</code> &mdash; hires graphics mode
</ul> </ul>
<dt>CALL <var>aexpr</var><dd>Call native routine <dt>CALL <var>aexpr</var><dd>Call native routine
<ul> <ul>
<li><code>CALL -3288</code> - pop <code>ONERR</code>/<code>RESUME</code> entry from stack <li><code>CALL 54915</code> or <code>CALL -10621</code> &mdash; clear stack (pop all <code>FOR</code>/<code>NEXT</code>, <code>GOSUB</code>/<code>RETURN</code>, and <code>ONERR</code>/<code>RESUME</code> entries)
<li><code>CALL -3100</code> - reveal hi-res page 1 <li><code>CALL 62248</code> or <code>CALL -3288</code> &mdash; pop <code>ONERR</code>/<code>RESUME</code> entry from stack
<li><code>CALL -3086</code> - clear current hi-res page to black <li><code>CALL 62436</code> or <code>CALL -3100</code> &mdash; reveal hi-res page 1
<li><code>CALL -3082</code> - clear current hi-res page to current color <li><code>CALL 62450</code> or <code>CALL -3086</code> &mdash; clear current hi-res page to black
<li><code>CALL 54951</code> - clear stack (pop all <code>FOR</code>/<code>NEXT</code>, <code>GOSUB</code>/<code>RETURN</code>, and <code>ONERR</code>/<code>RESUME</code> entries) <li><code>CALL 62454</code> or <code>CALL -3082</code> &mdash; clear current hi-res page to current color
<li><code>CALL -1036</code> - move cursor right <li><code>CALL 64500</code> or <code>CALL -1036</code> &mdash; move cursor right
<li><code>CALL -1008</code> - move cursor left <li><code>CALL 64528</code> or <code>CALL -1008</code> &mdash; move cursor left
<li><code>CALL -998</code> - move cursor up <li><code>CALL 64538</code> or <code>CALL -998</code> &mdash; move cursor up
<li><code>CALL -958</code> - clear text from cursor to bottom of window <li><code>CALL 64578</code> or <code>CALL -958</code> &mdash; clear text from cursor to bottom of window
<li><code>CALL -922</code> - move cursor down <li><code>CALL 64614</code> or <code>CALL -922</code> &mdash; move cursor down
<li><code>CALL -868</code> - clear text from cursor to end of line <li><code>CALL 64668</code> or <code>CALL -868</code> &mdash; clear text from cursor to end of line
<li><code>CALL 64780</code> or <code>CALL -756</code> &mdash; wait for keypress
<li><code>CALL 65152</code> or <code>CALL -384</code> &mdash; set inverse text mode
<li><code>CALL 65156</code> or <code>CALL -380</code> &mdash; set normal text mode
</ul> </ul>
<dt>PR# <var>aexpr</var><dd>Direct output to slot <dt>PR# <var>aexpr</var><dd>Direct output to slot
<ul> <ul>
<li><code>PR#0</code> - set 40 column mode <li><code>PR#0</code> &mdash; set 40 column mode
<li><code>PR#3</code> - set 80 column mode <li><code>PR#3</code> &mdash; set 80 column mode
</ul> </ul>
</dl> </dl>
@ -287,22 +290,22 @@ can be literals (unquoted strings), strings, or numbers
<dl> <dl>
<dt id="peek">PEEK( <var>aexpr</var> )<dd>Value at memory location <dt id="peek">PEEK( <var>aexpr</var> )<dd>Value at memory location
<ul> <ul>
<li><code>PEEK(32)</code> - Text window left edge <li><code>PEEK(32)</code> &mdash; Text window left edge
<li><code>PEEK(33)</code> - Text window width <li><code>PEEK(33)</code> &mdash; Text window width
<li><code>PEEK(34)</code> - Text window top edge <li><code>PEEK(34)</code> &mdash; Text window top edge
<li><code>PEEK(35)</code> - Text window bottom <li><code>PEEK(35)</code> &mdash; Text window bottom
<li><code>PEEK(36)</code> - Text cursor x <li><code>PEEK(36)</code> &mdash; Text cursor x
<li><code>PEEK(37)</code> - Text cursor y <li><code>PEEK(37)</code> &mdash; Text cursor y
<li><code>PEEK(78)</code> &amp; <code>PEEK(79)</code> - Random-Number Field <li><code>PEEK(78)</code> &amp; <code>PEEK(79)</code> &mdash; Random-Number Field
<li><code>PEEK(222)</code> - Last error code <li><code>PEEK(222)</code> &mdash; Last error code
<li><code>PEEK(230)</code> - Hi-Res plotting page (32 = page 1, 64 = page 2) <li><code>PEEK(230)</code> &mdash; Hi-Res plotting page (32 = page 1, 64 = page 2)
<li><code>PEEK(49152)</code> - Read Keyboard <li><code>PEEK(49152)</code> &mdash; Read Keyboard
<li><code>PEEK(49168)</code> - Clear Keyboard strobe <li><code>PEEK(49168)</code> &mdash; Clear Keyboard strobe
<li><code>PEEK(49200)</code> - Click Speaker (no-op) <li><code>PEEK(49200)</code> &mdash; Click Speaker (no-op)
<li><code>PEEK(49248)</code> - Read Paddle Button #3 - <em>Use the PageDown key</em> <li><code>PEEK(49248)</code> &mdash; Read Paddle Button #3 &mdash; <em>Use the PageDown key</em>
<li><code>PEEK(49249)</code> - Read Paddle Button #0 - <em>Use the Home key</em> <li><code>PEEK(49249)</code> &mdash; Read Paddle Button #0 &mdash; <em>Use the Home key</em>
<li><code>PEEK(49250)</code> - Read Paddle Button #1 - <em>Use the End key</em> <li><code>PEEK(49250)</code> &mdash; Read Paddle Button #1 &mdash; <em>Use the End key</em>
<li><code>PEEK(49251)</code> - Read Paddle Button #2 - <em>Use the PageUp or Shift key</em> <li><code>PEEK(49251)</code> &mdash; Read Paddle Button #2 &mdash; <em>Use the PageUp or Shift key</em>
</ul> </ul>
</dl> </dl>
</section> </section>
@ -350,19 +353,19 @@ can be literals (unquoted strings), strings, or numbers
<dl> <dl>
<dt>0<dd>Next without for <dt>0<dd>Next without for
<dt>16<dd>Syntax error - <em>Not generated</em> <dt>16<dd>Syntax error &mdash; <em>Not generated</em>
<dt>22<dd>Return without gosub <dt>22<dd>Return without gosub
<dt>42<dd>Out of data <dt>42<dd>Out of data
<dt>53<dd>Illegal quantity <dt>53<dd>Illegal quantity
<dt>69<dd>Overflow <dt>69<dd>Overflow
<dt>77<dd>Out of memory - <em>Not generated by all browsers</em> <dt>77<dd>Out of memory &mdash; <em>Not generated by all browsers</em>
<dt>90<dd>Undefined statement <dt>90<dd>Undefined statement
<dt>107<dd>Bad subscript <dt>107<dd>Bad subscript
<dt>120<dd>Redimensioned array <dt>120<dd>Redimensioned array
<dt>133<dd>Division by zero <dt>133<dd>Division by zero
<dt>163<dd>Type mismatch <dt>163<dd>Type mismatch
<dt>176<dd>String too long - <em>Not generated</em> <dt>176<dd>String too long &mdash; <em>Not generated</em>
<dt>191<dd>Formula too complex - <em>Not generated by all browsers</em> <dt>191<dd>Formula too complex &mdash; <em>Not generated by all browsers</em>
<dt>224<dd>Undefined function <dt>224<dd>Undefined function
<dt>254<dd>Re-enter <dt>254<dd>Re-enter
<dt>255<dd>Break <dt>255<dd>Break
@ -404,20 +407,20 @@ can be literals (unquoted strings), strings, or numbers
<code>PEEK(222)</code> in an ONERR handler.</p> <code>PEEK(222)</code> in an ONERR handler.</p>
<dl> <dl>
<dt>1<dd>Language not available - <em>Not generated</em> <dt>1<dd>Language not available &mdash; <em>Not generated</em>
<dt>2<dd>Range error - <em>Not generated</em> <dt>2<dd>Range error &mdash; <em>Not generated</em>
<dt>4<dd>Write protected - <em>Not generated</em> <dt>4<dd>Write protected &mdash; <em>Not generated</em>
<dt>5<dd>End of data <dt>5<dd>End of data
<dt>6<dd>File not found <dt>6<dd>File not found
<dt>7<dd>Volume mismatch - <em>Not generated</em> <dt>7<dd>Volume mismatch &mdash; <em>Not generated</em>
<dt>8<dd>I/O error - <em>Not generated</em> <dt>8<dd>I/O error &mdash; <em>Not generated</em>
<dt>9<dd>Disk full - <em>Not generated</em> <dt>9<dd>Disk full &mdash; <em>Not generated</em>
<dt>10<dd>File locked - <em>Not generated</em> <dt>10<dd>File locked &mdash; <em>Not generated</em>
<dt>11<dd>Invalid option <dt>11<dd>Invalid option
<dt>12<dd>No buffers available - <em>Not generated</em> <dt>12<dd>No buffers available &mdash; <em>Not generated</em>
<dt>13<dd>File type mismatch - <em>Not generated</em> <dt>13<dd>File type mismatch &mdash; <em>Not generated</em>
<dt>14<dd>Program too large - <em>Not generated</em> <dt>14<dd>Program too large &mdash; <em>Not generated</em>
<dt>15<dd>Not direct command - <em>Not generated</em> <dt>15<dd>Not direct command &mdash; <em>Not generated</em>
</dl> </dl>
</section> </section>
@ -528,7 +531,7 @@ object representing the program.</p>
<code>separator</code>, <code>separator</code>,
<code>remark</code>, <code>remark</code>,
<code>data-declaration</code> <code>data-declaration</code>
- take a peek at the code if you want the gruesome details. Source lines may &mdash; take a peek at the code if you want the gruesome details. Source lines may
only start with line numbers or (as an extension) separators. Special statement only start with line numbers or (as an extension) separators. Special statement
parsing is done while lexing: <code>REM</code> consumes anything to the next parsing is done while lexing: <code>REM</code> consumes anything to the next
line break, and <code>DATA</code> statements yield an array of strings line break, and <code>DATA</code> statements yield an array of strings
@ -588,7 +591,7 @@ function which implements the logic for walking over the array.<p>
<li id="def_fn_string"> <li id="def_fn_string">
<code>DEF FN</code> supports string and integer functions <code>DEF FN</code> supports string and integer functions
e.g. <code>DEF FN IN$(X$) = " " + X$</code> e.g. <code>DEF FN IN$(X$) = " " + X$</code>
- the return type must match the argument type, so string-to-number or number-to-string functions &mdash; the return type must match the argument type, so string-to-number or number-to-string functions
can not be implemented. can not be implemented.
<li id="eqeq"> <li id="eqeq">
"Double equals" <code>==</code> is supported for equality comparisons, with the same meaning as "single equals" <code>=</code> "Double equals" <code>==</code> is supported for equality comparisons, with the same meaning as "single equals" <code>=</code>

View File

@ -9,14 +9,13 @@ h1, h2, h3, p, ul { margin-bottom: 0; margin-top: 0; }
/* "Paper" (for copying output) */ /* "Paper" (for copying output) */
#paper { #paper {
display: none;
margin: 0; margin: 0;
position: fixed; position: fixed;
z-index: 100; z-index: 100;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
top: 560px; height: 0;
overflow-x: hidden; overflow-x: hidden;
overflow-y: scroll; overflow-y: scroll;
font-family: Courier, Monospace; font-family: Courier, Monospace;
@ -28,10 +27,10 @@ h1, h2, h3, p, ul { margin-bottom: 0; margin-top: 0; }
padding-left: 50px; padding-left: 50px;
white-space: pre; white-space: pre;
box-shadow: inset 0 5px 10px black; box-shadow: inset 0 5px 10px black;
transition: height 0.5s ease;
} }
body.printout #paper { display: block; } body.printout #paper, #paper-spacer { height: 200px; }
#show_paper { display: inline-block; }
#hide_paper { display: none; } body:not(.printout) #hide_paper { display: none; }
body.printout #show_paper { display: none; } body.printout #show_paper { display: none; }
body.printout #hide_paper { display: inline-block; }