mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Files

On a device with a phone keypad, driving Guess by typing numbers rather than using the arrow keys seems natural. But if you're going to do that, showing the labels makes it a lot easier. KaiOS doesn't have any way for the user to control this, so we should default to the friendlier state.
357 lines
8.3 KiB
Perl
Executable File
357 lines
8.3 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
@ARGV == 2 or die "usage: apppage.pl <name> <displayname>";
|
|
my ($name, $displayname) = @ARGV;
|
|
|
|
my $prefs = "";
|
|
if ($name eq 'guess') {
|
|
$prefs = <<EOF
|
|
show-labels=true
|
|
EOF
|
|
}
|
|
|
|
print <<EOF;
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=ASCII" />
|
|
<meta name="theme-color" content="rgb(50,50,50)" />
|
|
<title>${displayname}</title>
|
|
<!-- The KaiAds scripts are only present in Kai Store versions. -->
|
|
<script src="kaiads.v5.min.js"></script>
|
|
<script defer type="text/javascript" src="${name}.js"></script>
|
|
<script defer type="text/javascript" src="kaiads-glue.js"></script>
|
|
<!-- Override some defaults for small screens -->
|
|
<script id="environment" type="application/json">
|
|
{ "PATTERN_DEFAULT": "10x10",
|
|
"PUZZLES_ALLOW_CUSTOM": false,
|
|
"PUZZLES_SHOW_CURSOR": true }
|
|
</script>
|
|
<script class="preferences" type="text/plain">
|
|
$prefs</script>
|
|
<style class="text/css">
|
|
body {
|
|
margin: 0;
|
|
display: flex;
|
|
position: fixed;
|
|
width: 100%;
|
|
top: 0;
|
|
bottom: 30px;
|
|
font-family: "Open Sans", sans-serif;
|
|
font-size: 17px;
|
|
}
|
|
|
|
/* Top-level form for the game menu */
|
|
#gamemenu {
|
|
/* Add a little mild text formatting */
|
|
font-weight: bold;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* 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 */
|
|
display: flex;
|
|
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;
|
|
/* 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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
#gamemenu ul li[role=separator] {
|
|
color: transparent;
|
|
border: 0;
|
|
}
|
|
|
|
/* The interactive contents of menu items are their child elements. */
|
|
#gamemenu li > * {
|
|
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-color: rgba(0,0,0,0.3);
|
|
}
|
|
|
|
.transient {
|
|
/* When they are displayed, they are positioned immediately above
|
|
* their parent <li>, and with the left edge aligning */
|
|
position: fixed;
|
|
bottom: 30px;
|
|
max-height: calc(100vh - 30px);
|
|
left: 100%;
|
|
transition: left 0.1s;
|
|
box-sizing: border-box;
|
|
width: 100vw;
|
|
overflow: auto;
|
|
/* And make sure they appear in front. */
|
|
z-index: 50;
|
|
}
|
|
|
|
.transient.focus-within {
|
|
/* Once a menu is actually focussed, bring it on screen. */
|
|
left: 0;
|
|
/* Hiding what's behind. */
|
|
box-shadow: 0 0 1em 0 rgba(0, 0, 0, 0.8);
|
|
}
|
|
|
|
/* #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;
|
|
border-radius: 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 {
|
|
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;
|
|
}
|
|
|
|
#puzzle {
|
|
background: var(--puzzle-background, #e6e6e6);
|
|
flex: 1 1 auto;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
display: flex;
|
|
width: 100%
|
|
}
|
|
|
|
#statusbar {
|
|
overflow: hidden;
|
|
text-align: left;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
line-height: 1;
|
|
background: #d8d8d8;
|
|
border-left: 2px solid #c8c8c8;
|
|
border-top: 2px solid #c8c8c8;
|
|
border-right: 2px solid #e8e8e8;
|
|
border-bottom: 2px solid #e8e8e8;
|
|
height: 1em;
|
|
}
|
|
|
|
#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;
|
|
}
|
|
|
|
#puzzlecanvascontain {
|
|
flex: 1 1 auto;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-width: 0;
|
|
min-height: 0;
|
|
}
|
|
|
|
#puzzlecanvas {
|
|
max-width: 100%;
|
|
max-height: 100%;
|
|
background-color: white;
|
|
font-weight: 600;
|
|
}
|
|
|
|
#puzzlecanvas:focus {
|
|
/* The focus will be here iff there's nothing else on
|
|
* screen that can be focused, so the outline is
|
|
* redundant. */
|
|
outline: none;
|
|
}
|
|
|
|
#puzzle > div {
|
|
width: 100%;
|
|
}
|
|
|
|
.softkey {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
height: 30px;
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
line-height: 1;
|
|
white-space: nowrap;
|
|
background: rgb(50,50,50);
|
|
color: white;
|
|
z-index: 150;
|
|
}
|
|
|
|
:not(.focus-within) > .softkey {
|
|
display: none;
|
|
}
|
|
|
|
.softkey > * {
|
|
position: absolute;
|
|
padding: 8px;
|
|
}
|
|
|
|
.lsk {
|
|
left: 0;
|
|
right: 70%;
|
|
text-align: left;
|
|
padding-right: 0;
|
|
}
|
|
|
|
.csk {
|
|
left: 30%;
|
|
right: 30%;
|
|
text-align: center;
|
|
text-transform: uppercase;
|
|
padding-left: 0;
|
|
padding-right: 0;
|
|
}
|
|
|
|
.rsk {
|
|
right: 0;
|
|
left: 70%;
|
|
text-align: right;
|
|
padding-left: 0
|
|
}
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="puzzle">
|
|
<div id="puzzlecanvascontain">
|
|
<canvas id="puzzlecanvas" width="1px" height="1px" tabindex="0">
|
|
</canvas>
|
|
</div>
|
|
<div id="statusbar">
|
|
</div>
|
|
<div class="softkey">
|
|
<div class="lsk"></div><div class="csk"></div>
|
|
<div class="rsk">Menu</div></div>
|
|
</div>
|
|
<form id="gamemenu" class="transient">
|
|
<ul>
|
|
<!--
|
|
<li><div tabindex="0">Game<ul class="transient">
|
|
<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>
|
|
</ul></div></li>
|
|
-->
|
|
<li><div tabindex="0">Type<ul id="gametype" class="transient"></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>
|
|
<li><a target="_blank" href="help/en/${name}.html#${name}">
|
|
Instructions
|
|
</a></li>
|
|
<li><a target="_blank" href="help/en/index.html">
|
|
Full manual
|
|
</a></li>
|
|
</ul>
|
|
<div class="softkey">
|
|
<div class="csk">Select</div>
|
|
<div class="rsk">Dismiss</div>
|
|
</div>
|
|
</form>
|
|
</body>
|
|
</html>
|
|
EOF
|