123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- #!/usr/bin/perl
- use strict;
- use warnings;
- my $jspath = "";
- while ($ARGV[0] =~ /^-/) {
- my $opt = shift @ARGV;
- last if $opt eq "--";
- if ($opt =~ /^--jspath=(.+)$/) {
- $jspath = $1;
- } else {
- die "jspage.pl: unrecognised option '$opt'\n";
- }
- }
- open my $footerfile, "<", shift @ARGV or die "footer: open: $!\n";
- my $footer = "";
- $footer .= $_ while <$footerfile>;
- close $footerfile;
- for my $arg (@ARGV) {
- $arg =~ /(.*\/)?([^\/]+)\.html$/ or die;
- my $filename = $2;
- open my $gamefile, "<", $arg or die "$arg: open: $!\n";
- my $unfinished = 0;
- my $docname = $filename;
- chomp(my $puzzlename = <$gamefile>);
- while ($puzzlename =~ s/^([^:=]+)(=([^:]+))?://) {
- if ($1 eq "unfinished") {
- $unfinished = 1;
- } elsif ($1 eq "docname") {
- $docname = $3;
- } else {
- die "$arg: unknown keyword '$1'\n";
- }
- }
- my $instructions = "";
- $instructions .= $_ while <$gamefile>;
- close $gamefile;
- open my $outpage, ">", "${filename}.html";
- my $unfinishedtitlefragment = $unfinished ? "an unfinished puzzle " : "";
- my $unfinishedheading = $unfinished ? "<h2 align=center>an unfinished puzzle</h2>\n" : "";
- my $unfinishedpara;
- my $links;
- if ($unfinished) {
- $unfinishedpara = <<EOF;
- <p>
- You have found your way to a page containing an <em>unfinished</em>
- puzzle in my collection, not linked from the <a href="../">main
- puzzles page</a>. Don't be surprised if things are hard to understand
- or don't work as you expect.
- EOF
- $links = <<EOF;
- <p align="center">
- <a href="../">Back to main puzzles page</a> (which does not link to this)
- EOF
- } else {
- $unfinishedpara = "";
- $links = <<EOF;
- <p align="center">
- <a href="../doc/${docname}.html#${docname}">Full instructions</a>
- |
- <a href="../">Back to main puzzles page</a>
- EOF
- }
- print $outpage <<EOF;
- <!DOCTYPE html>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=ASCII" />
- <title>${puzzlename}, ${unfinishedtitlefragment}from Simon Tatham's Portable Puzzle Collection</title>
- <script defer type="text/javascript" src="${jspath}${filename}.js"></script>
- <style>
- /* Top-level form for the game menu */
- #gamemenu {
- margin-top: 0;
- margin-bottom: 0.375em;
- /* Add a little mild text formatting */
- font-weight: bold;
- font-size: 0.8em;
- text-align: center
- }
- main {
- text-align: center;
- }
- /* Inside that form, the main menu bar and every submenu inside it is a <ul> */
- #gamemenu ul {
- list-style: none; /* get rid of the normal unordered-list bullets */
- /* make top-level menu bar items appear side by side */
- display: flex;
- flex-wrap: wrap;
- justify-content: center;
- margin: 0;
- /* Compensate for the negative margins on menu items by adding a
- * little bit of padding so that the borders of the items don't protrude
- * beyond the menu. */
- padding: 0.5px;
- }
- /* Individual menu items are <li> elements within such a <ul> */
- #gamemenu li {
- /* Suppress the text-selection I-beam pointer */
- cursor: default;
- /* Surround each menu item with a border. */
- border: 1px solid rgb(180,180,180);
- /* Arrange that the borders of each item overlap the ones next to it. */
- margin: -0.5px;
- /* Set position:relative, so that if this item has a submenu it can
- * position itself relative to the parent item. */
- position: relative;
- }
- #gamemenu li[role=separator] {
- width: 1.5em;
- border: 0;
- }
- /* The interactive contents of menu items are their child elements. */
- #gamemenu li > * {
- /* Line height and padding appropriate to top-level menu items */
- padding: 0.2em 0.75em;
- margin: 0;
- display: block;
- }
- #gamemenu :disabled {
- /* Grey out disabled buttons */
- color: rgba(0,0,0,0.5);
- }
- #gamemenu li > :hover:not(:disabled),
- #gamemenu li > .focus-within {
- /* When the mouse is over a menu item, highlight it */
- background: rgba(0,0,0,0.3);
- }
- \@media (max-width:18em) {
- /* Suppress some words in top-level menu items when viewport
- * is very small */
- .verbiage {
- display: none;
- }
- }
- #gamemenu ul ul {
- /* Second-level menus and below are not displayed by default */
- display: none;
- /* When they are displayed, they are positioned immediately below
- * their parent <li>, and with the left edge aligning */
- position: absolute;
- top: 100%;
- left: 0;
- /* Switch to vertical stacking for drop-down submenus */
- flex-direction: column;
- /* We must specify an explicit background colour for submenus, because
- * they must be opaque (don't want other page contents showing through
- * them). */
- background: white;
- /* And make sure they appear in front. */
- z-index: 50;
- }
- #gamemenu ul ul.left {
- /* A second-level menu with class "left" aligns its right edge with
- * its parent, rather than its left edge */
- left: inherit; right: 0;
- }
- /* Menu items in second-level menus and below */
- #gamemenu li li {
- /* Inhibit wrapping, so the submenu will expand its width as needed. */
- white-space: nowrap;
- /* Override the text-align:center from above */
- text-align: left;
- }
- #gamemenu ul ul ul {
- /* Third-level submenus are drawn to the side of their parent menu
- * item, not below it */
- top: 0; left: 100%;
- }
- #gamemenu ul ul ul.left {
- /* A submenu with class "left" goes to the left of its parent,
- * not the right */
- left: inherit; right: 100%;
- }
- #gamemenu :hover > ul,
- #gamemenu .focus-within > ul {
- /* Last but by no means least, the all-important line that makes
- * submenus be displayed! Any <ul> whose parent <li> is being
- * hovered over gets display:flex overriding the display:none
- * from above. */
- display: flex;
- }
- #gamemenu button {
- /* Menu items that trigger an action. We put some effort into
- * removing the default button styling. */
- -moz-appearance: none;
- -webkit-appearance: none;
- appearance: none;
- font: inherit;
- color: inherit;
- background: initial;
- border: initial;
- text-align: inherit;
- width: 100%;
- }
- #gamemenu .tick {
- /* The tick at the start of a menu item, or its unselected equivalent.
- * This is represented by an <input type="radio">, so we put some
- * effort into overriding the default style. */
- -moz-appearance: none;
- -webkit-appearance: none;
- appearance: none;
- margin: initial;
- font: inherit;
- }
- #gamemenu .tick::before {
- content: "\\2713";
- }
- #gamemenu .tick:not(:checked) {
- /* Tick for an unselected menu entry. */
- color: transparent;
- }
- #gamemenu li > div:after {
- /* Downward arrow for submenu headings at the top level. */
- content: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='10'%20height='10'%3E%3Cpolygon%20points='0,5,10,5,5,10'/%3E%3C/svg%3E");
- margin-left: 0.5em;
- }
- #gamemenu li li > div:after {
- /* Rightward arrow marker for submenus on lower-level menus. */
- content: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='10'%20height='10'%3E%3Cpolygon%20points='0,0,10,5,0,10'/%3E%3C/svg%3E");
- float: right;
- }
- #statusbar {
- overflow: hidden;
- text-align: left;
- white-space: nowrap;
- text-overflow: ellipsis;
- background: #d8d8d8;
- border-left: 2px solid #c8c8c8;
- border-top: 2px solid #c8c8c8;
- border-right: 2px solid #e8e8e8;
- border-bottom: 2px solid #e8e8e8;
- height: 1.2em;
- }
- #dlgdimmer {
- width: 100%;
- height: 100%;
- background: #000000;
- position: fixed;
- opacity: 0.3;
- left: 0;
- top: 0;
- z-index: 99;
- }
- #dlgform {
- width: 66.6667vw;
- opacity: 1;
- background: #ffffff;
- color: #000000;
- position: absolute;
- border: 2px solid black;
- padding: 20px;
- top: 10vh;
- left: 16.6667vw;
- z-index: 100;
- }
- #dlgform h2 {
- margin-top: 0px;
- }
- #resizehandle {
- position: absolute;
- z-index: 1;
- bottom: 0;
- right: 0;
- cursor: se-resize;
- }
- #resizable {
- position: relative;
- margin: 0 auto;
- }
- #puzzlecanvas {
- display: block;
- width: 100%;
- /* This sets the font that will be used in the puzzle. */
- font-family: sans-serif;
- }
- #apology {
- padding: 0 1em 0 1em;
- margin: 1em;
- border: 2px solid red;
- }
- .apology-title {
- text-align: center;
- }
- \@media print {
- /* Interactive controls should be hidden when printing. */
- #gamemenu, #resizehandle { display: none; }
- }
- </style>
- </head>
- <body>
- <h1 align=center>${puzzlename}</h1>
- ${unfinishedheading}
- <h2 align=center>from Simon Tatham's Portable Puzzle Collection</h2>
- ${unfinishedpara}
- <hr>
- <main id="puzzle" style="display: none">
- <form id="gamemenu"><ul>
- <li><div tabindex="0">Game<ul>
- <li><button type="button" id="specific">Enter game ID...</button></li>
- <li><button type="button" id="random">Enter random seed...</button></li>
- <li><button type="button" id="save">Download save file...</button></li>
- <li><button type="button" id="load">Upload save file...</button></li>
- <li><button type="button" id="prefs">Preferences...</button></li>
- </ul></div></li>
- <li><div tabindex="0">Type<ul role="menu" id="gametype"></ul></div></li>
- <li role="separator"></li>
- <li><button type="button" id="new">
- New<span class="verbiage"> game</span>
- </button></li>
- <li><button type="button" id="restart">
- Restart<span class="verbiage"> game</span>
- </button></li>
- <li><button type="button" id="undo">
- Undo<span class="verbiage"> move</span>
- </button></li>
- <li><button type="button" id="redo">
- Redo<span class="verbiage"> move</span>
- </button></li>
- <li><button type="button" id="solve">
- Solve<span class="verbiage"> game</span>
- </button></li>
- </ul></form>
- <div id="resizable">
- <canvas id="puzzlecanvas" tabindex="0"></canvas>
- <div id="statusbar"></div>
- <img id="resizehandle" alt="resize"
- title="Drag to resize the puzzle. Right-click to restore the default size."
- src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='10'%20height='10'%3E%3Cpath%20d='M8.5,1.5l-7,7m7,-4l-4,4m4,-1l-1,1'%20stroke='black'%20stroke-linecap='round'/%3E%3C/svg%3E">
- </div>
- <p>
- Link to this puzzle:
- <a id="permalink-desc">by game ID</a>
- <a id="permalink-seed">by random seed</a>
- </p>
- </main>
- <div id="apology">
- <p class="apology-title">If you've been reading this message for more
- than a second or two, then <strong>this WebAssembly puzzle doesn't
- seem to be working in your web browser</strong>. Sorry!</p>
- <p>
- <details>
- <summary>More information</summary>
- <p>Most modern browsers should support WASM. I've had reports of success from:
- <ul>
- <li>Firefox 87.0</li>
- <li>Chrome 89.0.4389.114</li>
- <li>Safari 14.0.3 (16610.4.3.1.7)</li>
- <li>Edge 89.0.774.68</li>
- <li>Opera 75.0.3969.149</li>
- </ul></p>
- <p>If this puzzle isn't working in one of those browsers (or a later
- version), check to see whether you have any local configuration that
- might have turned off WebAssembly completely, or some kind of blocking
- software that might have prevented the WASM code from being downloaded
- in the first place.</p>
- <p>(For example, in Firefox, it's possible to turn off WASM completely
- by setting <code>javascript.options.wasm</code> to <code>false</code>
- in the <code>about:config</code> interface. If you've done that, or
- something analogous in another browser, this puzzle won't run.)</p>
- <p>In other browsers, the problem might be that WebAssembly isn't
- supported at all (for example, Internet Explorer 11), or that a
- browser update is needed.</p>
- <p>If you think that your browser <em>should</em> support WebAssembly,
- but this puzzle still isn't running, then please report the problem,
- including <strong>as much diagnostic information as you can
- find</strong>.</p>
- <p>In particular, try opening your browser's Javascript error console
- and then reloading this page, and tell me if it reports any error
- messages.</p>
- <p>Also, if your browser has a network diagnostic tab, try the same
- experiment, to make sure it is successfully loading both of the
- auxiliary files <code>${filename}.js</code> and
- <code>${filename}.wasm</code>.</p>
- </details>
- </p>
- </div>
- <hr>
- ${instructions}
- ${links}
- ${footer}
- </body>
- </html>
- EOF
- close $outpage;
- }
|