Add initial files
This commit is contained in:
commit
47ccd46ac5
315
morse-code.htm
Normal file
315
morse-code.htm
Normal file
@ -0,0 +1,315 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"><head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="author" content="Alex Ellis">
|
||||
|
||||
<meta name="generator" content="Hugo 0.64.1">
|
||||
<title>Little games to play with Morse Code in your browser · Caffeinspiration</title>
|
||||
<link rel="shortcut icon" href="https://alexanderell.is/images/favicon.ico">
|
||||
<link rel="stylesheet" href="morse-code_files/style.css">
|
||||
<link rel="stylesheet" href="morse-code_files/highlight.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="morse-code_files/font-awesome.css">
|
||||
|
||||
|
||||
|
||||
<link href="https://alexanderell.is/index.xml" rel="alternate" type="application/rss+xml" title="Caffeinspiration">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="main-nav">
|
||||
|
||||
|
||||
<a href="https://alexanderell.is/"> <span class="arrow">←</span>Home</a>
|
||||
|
||||
<a href="https://alexanderell.is/posts">Archive</a>
|
||||
<a href="https://alexanderell.is/tags">Tags</a>
|
||||
<a href="https://alexanderell.is/about">About</a>
|
||||
|
||||
|
||||
|
||||
|
||||
<a class="cta" href="https://alexanderell.is/index.xml">RSS</a>
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
<section id="wrapper">
|
||||
<article class="post">
|
||||
<header>
|
||||
<h1>
|
||||
Little games to play with Morse Code in your browser
|
||||
</h1>
|
||||
<h2>
|
||||
|
||||
</h2>
|
||||
<h2 class="headline">
|
||||
Feb 4, 2022
|
||||
· 728 words
|
||||
· 4 minutes read
|
||||
<span class="tags">
|
||||
|
||||
</span>
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<section id="post-body">
|
||||
<p>This is a pair of games for playing with Morse
|
||||
Code. Since the games play the signals as sound, I recommend headphones
|
||||
(wired if possible for the latency) and a low volume. Game options are
|
||||
available towards the bottom.</p>
|
||||
<link rel="stylesheet" href="morse-code_files/styles.css">
|
||||
<noscript>
|
||||
Unfortunately, JavaScript is needed to run these games. The only JS running on my site is for the games (no tracking, etc) if you want to enable it. The good news is that if you're disabling JavaScript, you probably already know some Morse Code :)
|
||||
</noscript>
|
||||
<h1 id="morse-code-listening-game">Morse Code Listening Game</h1>
|
||||
<p>In this game, it will play a word in Morse Code, and you have to type in the word.</p>
|
||||
<br>
|
||||
<div class="game-container">
|
||||
<button id="playListeningGame" class="start-button">Start</button>
|
||||
<br>
|
||||
<p id="status" class="display-message">Press Start to begin.</p>
|
||||
<input type="text" id="wordInput" class="input" placeholder="Enter message" autocomplete="off" disabled="disabled">
|
||||
<br>
|
||||
<div class="button-container">
|
||||
<button id="submitButton" class="interaction-button" disabled="disabled">Enter</button>
|
||||
<button id="resetButton" class="interaction-button" disabled="disabled">Replay</button>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<details>
|
||||
<summary>How to play the listening game</summary>
|
||||
<br>
|
||||
<p>As you hear the Morse Code, enter the letters (or words!) that you
|
||||
hear. Press Enter (either the button or on your keyboard) to see if you
|
||||
got it, and press Replay (or Control on your keyboard) to hear it again.</p>
|
||||
<p>For doing Morse to letter lookups, I find it helpful to look at a
|
||||
tree-shaped diagram, where you can follow from one node to the next
|
||||
based on whether it’s a dit or a dah.
|
||||
<a href="https://upload.wikimedia.org/wikipedia/commons/1/19/Morse-code-tree.svg" target="blank_">
|
||||
<img src="morse-code_files/Morse-code-tree.svg" alt="Binary Tree-shaped diagram of letters and their corresponding Morse Code representation.">
|
||||
</a>
|
||||
<a href="https://commons.wikimedia.org/wiki/File:Morse-code-tree.svg">Source</a></p>
|
||||
|
||||
</details>
|
||||
<br>
|
||||
<h1 id="morse-code-speaking-game">Morse Code “Speaking” Game</h1>
|
||||
<p>For the speaking game, it will show a word, and you’ll have to enter it in Morse Code.</p>
|
||||
<div class="game-container">
|
||||
<button id="playInputGame" class="start-button">Start</button>
|
||||
<p id="inputStatus" class="display-message"></p>
|
||||
<p id="targetDisplay" class="display-message">Press Start to begin.</p>
|
||||
<p id="inputDisplay" class="display-message morse-input"></p>
|
||||
<div class="button-container">
|
||||
<button id="signalButton" class="interaction-button" disabled="disabled">Tap</button>
|
||||
<button id="startOverButton" class="interaction-button" disabled="disabled">Try Again</button>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<details>
|
||||
<summary>How to play the speaking game</summary>
|
||||
<p>Press start to reveal the target. Enter Morse Code with your
|
||||
spacebar or the Tap button. Press Control or the Try Again button to
|
||||
clear your input and start over.</p>
|
||||
<p>The game will assume you’re done with a letter after the letter gap
|
||||
interval (see options below), and it will automatically insert a space
|
||||
for you after the word interval; be careful you don’t take too long in
|
||||
between dits!</p>
|
||||
<p>For doing letter to Morse lookups, I find it helpful to look at an
|
||||
alphabetical list with the matching representation. This makes looking
|
||||
up a letter faster.
|
||||
<a href="https://upload.wikimedia.org/wikipedia/commons/1/19/Morse-code-tree.svg" target="blank_">
|
||||
<img src="morse-code_files/International_Morse_Code.svg" alt="Alphabetical list of letters and their corresponding Morse Code representation">
|
||||
</a>
|
||||
<a href="https://en.wikipedia.org/wiki/File:International_Morse_Code.svg">Source</a></p>
|
||||
|
||||
</details>
|
||||
<br>
|
||||
<h2 id="game-options">Game options</h2>
|
||||
<p>After changing settings, you have to stop and restart the above games.</p>
|
||||
<h3 id="difficulty">Difficulty</h3>
|
||||
<legend>Please select your preferred difficulty:</legend>
|
||||
<div>
|
||||
<input type="radio" id="difficultyEasy" name="difficulty" value="easy" checked="checked">
|
||||
<label for="difficultyEasy">Easy (letters)</label>
|
||||
<br>
|
||||
<input type="radio" id="difficultyMedium" name="difficulty" value="medium">
|
||||
<label for="difficultyMedium">Medium (short words)</label>
|
||||
<br>
|
||||
<input type="radio" id="difficultyHard" name="difficulty" value="hard">
|
||||
<label for="difficultyHard">Hard (multiple short words)</label>
|
||||
<br>
|
||||
<input type="radio" id="difficultyVeryHard" name="difficulty" value="really-hard">
|
||||
<label for="difficultyVeryHard">Very Hard (multiple words)</label>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<h3 id="speed">Speed</h3>
|
||||
<legend>Please select your preferred speed:</legend>
|
||||
<div>
|
||||
<input type="radio" id="speedEasy" name="speed" value="easy" checked="checked">
|
||||
<label for="speedEasy">Slow</label>
|
||||
<br>
|
||||
<input type="radio" id="speedMedium" name="speed" value="medium">
|
||||
<label for="speedMedium">Medium</label>
|
||||
<br>
|
||||
<input type="radio" id="speedHard" name="speed" value="hard">
|
||||
<label for="speedHard">Fast</label>
|
||||
<br>
|
||||
<input type="radio" id="speedHarder" name="speed" value="harder">
|
||||
<label for="speedHarder">Faster</label>
|
||||
</div>
|
||||
<br>
|
||||
<legend>Use the Farnsworth method?</legend>
|
||||
<div>
|
||||
<input type="radio" id="yesFarnsworth" name="farnsworth" value="yes" checked="checked">
|
||||
<label for="yesFarnsworth">Yes</label>
|
||||
<br>
|
||||
<input type="radio" id="noFarnsworth" name="farnsworth" value="no">
|
||||
<label for="noFarnsworth">No</label>
|
||||
</div>
|
||||
<br>
|
||||
<p>The speeds match the following from Wikipedia:</p>
|
||||
<p><em>The duration of a dah is three times the duration of a dit. Each
|
||||
dit or dah within an encoded character is followed by a period of signal
|
||||
absence, called a space, equal to the dit duration. The letters of a
|
||||
word are separated by a space of duration equal to three dits, and words
|
||||
are separated by a space equal to seven dits.</em></p>
|
||||
<div id="speed-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Speed</th>
|
||||
<th>Dit (ms)</th>
|
||||
<th>Dash (ms)</th>
|
||||
<th>Letter gap (ms)</th>
|
||||
<th>Word gap (ms)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Slow</td>
|
||||
<td>300</td>
|
||||
<td>900</td>
|
||||
<td>900</td>
|
||||
<td>2100</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Medium</td>
|
||||
<td>200</td>
|
||||
<td>600</td>
|
||||
<td>600</td>
|
||||
<td>1400</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fast</td>
|
||||
<td>100</td>
|
||||
<td>300</td>
|
||||
<td>300</td>
|
||||
<td>700</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Very Fast</td>
|
||||
<td>60</td>
|
||||
<td>180</td>
|
||||
<td>180</td>
|
||||
<td>420</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p>But, that being said, the <a href="https://www.google.com/search?q=Farnsworth+method">Farnsworth method</a>
|
||||
varies the intervals between the letters and the word gap instead of
|
||||
varying the length of the dits, dashes, or character gaps. It is more
|
||||
helpful for learning the sounds of each letter at a real speed you would
|
||||
hear it.</p>
|
||||
<p>All times are in milliseconds. I recommend starting with slow and
|
||||
giving yourself plenty of time for the dahs, then increasing the speed
|
||||
as you go!</p>
|
||||
<br>
|
||||
<h2 id="issuesquestions">Issues/questions</h2>
|
||||
<h4 id="something-went-wrong"><em>Something went wrong?</em></h4>
|
||||
<p>You may need to refresh — it’s a little rough around the edges.</p>
|
||||
<h4 id="something-went-wrong-on-ios"><em>Something went wrong on iOS?</em></h4>
|
||||
<p>First, make sure your phone is unsilenced (turns out a silenced phone
|
||||
blocks the web audio APIs). You also may need to tap Start and Stop a
|
||||
few times, refresh, or start with the “speaking” game. I ran into an
|
||||
issue with initializing the AudioContext, and although I think I got it
|
||||
right, I’m sorry if it’s giving you trouble.</p>
|
||||
<h4 id="why-is-it-so-quick-with-the-spaces-when-listening"><em>Why is it so quick with the spaces when listening?</em></h4>
|
||||
<p>Sorry, it was easiest to be super strict about the timing when making it. Try slowing the speed down or practicing to go faster!</p>
|
||||
<h4 id="how-does-it-work"><em>How does it work?</em></h4>
|
||||
<p>You can read about it <a href="https://alexanderell.is/posts/writing-morse-code-games">here</a>, and you can see the source code <a href="https://alexanderell.is/posts/morse-code/morse-code.js">here</a> and <a href="https://alexanderell.is/posts/morse-code/constants.js">here</a>, though it’s very unpolished.</p>
|
||||
<h4 id="any-recommendations-for-learning-morse-code-for-real"><em>Any recommendations for learning Morse Code for real?</em></h4>
|
||||
<p>I’m far from an expert, but so far I’ve seen a few recommendations (thanks to curiousfab and SaberTail on HN):</p>
|
||||
<ul>
|
||||
<li><a href="https://morsecode.world/international/trainer/trainer.html">https://morsecode.world/international/trainer/trainer.html</a></li>
|
||||
<li><a href="https://lcwo.net/">https://lcwo.net/</a></li>
|
||||
<li><a href="http://www.elkins.org/">http://www.elkins.org/</a></li>
|
||||
</ul>
|
||||
<script src="morse-code_files/constants.js"></script>
|
||||
<script src="morse-code_files/morse-code.js"></script>
|
||||
|
||||
</section>
|
||||
</article>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<footer id="footer">
|
||||
|
||||
<div id="social">
|
||||
|
||||
|
||||
|
||||
<a class="symbol" href="https://github.com/alexanderellis">
|
||||
<i class="fa fa-github-square"></i>
|
||||
</a>
|
||||
|
||||
<a class="symbol" href="https://alexanderell.is/">
|
||||
<i class="fa fa-home"></i>
|
||||
</a>
|
||||
|
||||
<a class="symbol" href="https://twitter.com/lxmkls">
|
||||
<i class="fa fa-twitter-square"></i>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<p class="small">
|
||||
|
||||
© Copyright 2022
|
||||
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
</section>
|
||||
|
||||
<script src="morse-code_files/jquery-2.js"></script>
|
||||
<script src="morse-code_files/main.js"></script>
|
||||
<script src="morse-code_files/highlight.js"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body></html>
|
1337
morse-code_files/International_Morse_Code.svg
Normal file
1337
morse-code_files/International_Morse_Code.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 27 KiB |
212
morse-code_files/Morse-code-tree.svg
Normal file
212
morse-code_files/Morse-code-tree.svg
Normal file
@ -0,0 +1,212 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" width="836" height="362" viewBox="0 0 836 362"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xml:space="preserve">
|
||||
<style type="text/css">
|
||||
circle, line, path{
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
stroke-width: 2.5;
|
||||
}
|
||||
circle{
|
||||
stroke-width: 1.5;
|
||||
}
|
||||
text{
|
||||
font-size: 28px;
|
||||
font-family: "Bitstream Charter", Charter, serif;
|
||||
text-anchor: middle;
|
||||
}
|
||||
.arrowhead{
|
||||
fill: #000;
|
||||
stroke-width: 3.2;
|
||||
}
|
||||
.big { stroke-width: 3; font-size: 32px; }
|
||||
.dot { stroke-dasharray: 2.5, 7.5; }
|
||||
.dash { stroke-dasharray: 7.5, 7.5; }
|
||||
.thin { stroke-width: 1; }
|
||||
.grey { stroke: #808080; }
|
||||
.tiny { font-size: 20px; }
|
||||
</style>
|
||||
<circle r="18" cx="209.1975" cy="96.7551"/>
|
||||
<circle r="18" cx="626.7500" cy="96.8076"/>
|
||||
<path class="dot" d="M384.1785583,48.2296791l-157.529892,43.7463417"/>
|
||||
<path class="dash" d="M451.8214417,48.2296791l157.5299377,43.7463417"/>
|
||||
<circle r="18" cx="104.8225" cy="156.279"/>
|
||||
<circle r="18" cx="313.5725" cy="156.279"/>
|
||||
<circle r="18" cx="522.3225" cy="156.279"/>
|
||||
<circle r="18" cx="731.0725" cy="156.279"/>
|
||||
<circle r="18" cx="70.03075" cy="224.2428"/>
|
||||
<circle r="18" cx="139.614" cy="224.2428"/>
|
||||
<circle r="18" cx="278.7808" cy="224.2428"/>
|
||||
<circle r="18" cx="348.364" cy="224.2428"/>
|
||||
<circle r="18" cx="487.5308" cy="224.2428"/>
|
||||
<circle r="18" cx="557.114" cy="224.2428"/>
|
||||
<circle r="18" cx="696.2801" cy="224.2428"/>
|
||||
<circle r="18" cx="765.864" cy="224.2428"/>
|
||||
<circle r="18" cx="793.6975" cy="284.211" class="grey"/>
|
||||
<circle r="18" cx="751.9475" cy="284.211" class="grey"/>
|
||||
<circle r="18" cx="710.1975" cy="284.211"/>
|
||||
<circle r="18" cx="668.4475" cy="284.211"/>
|
||||
<circle r="18" cx="584.9475" cy="284.211"/>
|
||||
<circle r="18" cx="543.1975" cy="284.211"/>
|
||||
<circle r="18" cx="501.4475" cy="284.211"/>
|
||||
<circle r="18" cx="459.6975" cy="284.211"/>
|
||||
<circle r="18" cx="376.1975" cy="284.211"/>
|
||||
<circle r="18" cx="334.4475" cy="284.211"/>
|
||||
<circle r="18" cx="292.6975" cy="284.211" class="grey"/>
|
||||
<circle r="18" cx="250.9475" cy="284.211"/>
|
||||
<circle r="18" cx="167.4475" cy="284.211" class="grey"/>
|
||||
<circle r="18" cx="125.6975" cy="284.211"/>
|
||||
<circle r="18" cx="83.9475" cy="284.211"/>
|
||||
<circle r="18" cx="42.1975" cy="284.211"/>
|
||||
<circle r="10" cx="24.162" cy="344.255" class="thin"/>
|
||||
<circle r="10" cx="47.35625" cy="344.255" class="thin"/>
|
||||
<circle r="10" cx="70.55075" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="93.74525" cy="344.255" class="thin"/>
|
||||
<circle r="10" cx="116.93975" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="140.134" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="163.3285" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="186.523" cy="344.255" class="thin"/>
|
||||
<circle r="10" cx="232.912" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="256.10625" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="279.30075" cy="344.255" class="thin"/>
|
||||
<circle r="10" cx="302.49525" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="325.68975" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="348.884" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="372.0785" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="395.273" cy="344.255" class="thin"/>
|
||||
<circle r="10" cx="441.66175" cy="344.255" class="thin"/>
|
||||
<circle r="10" cx="464.85625" cy="344.255" class="thin"/>
|
||||
<circle r="10" cx="488.05075" cy="344.255" class="thin"/>
|
||||
<circle r="10" cx="511.24525" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="534.43975" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="557.63425" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="580.8285" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="604.023" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="650.412" cy="344.255" class="thin"/>
|
||||
<circle r="10" cx="673.60625" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="696.80075" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="719.99525" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="743.18975" cy="344.255" class="thin"/>
|
||||
<circle r="10" cx="766.384" cy="344.255" class="thin grey"/>
|
||||
<circle r="10" cx="789.5785" cy="344.255" class="thin"/>
|
||||
<circle r="10" cx="812.773" cy="344.255" class="thin"/>
|
||||
<path class="dot" d="M193.5657043,105.7522507l-73.0063782,41.6347122"/>
|
||||
<path class="dash" d="M224.9343567,105.7522507l73.0063171,41.6347122"/>
|
||||
<path class="dot" d="M611.0656738,105.7522507l-73.0063477,41.6347122"/>
|
||||
<path class="dash" d="M642.4343262,105.7522507l73.0063477,41.6347122"/>
|
||||
<path class="dash" d="M739.3528442,172.4042816l18.3359375,35.8184357"/>
|
||||
<path class="dot" d="M722.8971558,172.4042816l-18.3359985,35.8184357"/>
|
||||
<path class="dash" d="M530.6028442,172.4042816l18.3359375,35.8184357"/>
|
||||
<path class="dot" d="M514.1471558,172.4042816l-18.335968,35.8184357"/>
|
||||
<path class="dash" d="M321.8528442,172.4042816l18.335968,35.8184357"/>
|
||||
<path class="dot" d="M305.3971558,172.4042816l-18.335968,35.8184357"/>
|
||||
<path class="dash" d="M113.1028519,172.4042816l18.3359756,35.8184357"/>
|
||||
<path class="dot" d="M96.6471481,172.4042816l-18.3359604,35.8184357"/>
|
||||
<path class="dot" d="M62.4814339,240.674057l-12.629528,27.2108612"/>
|
||||
<path class="dash" d="M74.1651917,241.8845062l5.7529526,24.7899933"/>
|
||||
<path class="dot" d="M135.5848236,241.8845062l-5.7529449,24.7899933"/>
|
||||
<path class="dash" d="M147.2685852,240.674057l12.6295471,27.2108612"/>
|
||||
<path class="dot" d="M271.2314453,240.674057l-12.6295166,27.2108612"/>
|
||||
<path class="dash" d="M282.9151917,241.8845062l5.7529297,24.7899628"/>
|
||||
<path class="dot" d="M344.3348083,241.8845062l-5.7529602,24.7899933"/>
|
||||
<path class="dash" d="M356.0185852,240.674057l12.6295166,27.2108612"/>
|
||||
<path class="dot" d="M479.9814148,240.674057l-12.6295166,27.2108612"/>
|
||||
<path class="dash" d="M491.6651917,241.8845062l5.7529297,24.7899628"/>
|
||||
<path class="dot" d="M553.0847778,241.8845062l-5.7529297,24.7899628"/>
|
||||
<path class="dash" d="M564.7685547,240.674057l12.6295166,27.2108612"/>
|
||||
<path class="dot" d="M688.7314453,240.674057l-12.6295166,27.2108612"/>
|
||||
<path class="dash" d="M700.4151611,241.8845062l5.7529907,24.7899933"/>
|
||||
<path class="dot" d="M761.8348389,241.8845062l-5.7529297,24.7899628"/>
|
||||
<path class="dash" d="M773.5185547,240.674057l12.6295166,27.2108612"/>
|
||||
<path class="dash" d="M799.1239624,301.502655l10.3438721,33.1818848"/>
|
||||
<path class="dot" d="M792.34729,302.2623291l-2.4954834,32.0210571"/>
|
||||
<path class="grey dash" d="M756.1107788,301.8459167l7.6352539,32.6573486"/>
|
||||
<path class="dot" d="M749.2194214,302.1032104l-5.0289307,32.2642212"/>
|
||||
<path class="grey dash" d="M713.0306396,302.1032104l5.0289307,32.2642212"/>
|
||||
<path class="grey dot" d="M706.1392822,301.8459167l-7.6353149,32.6573486"/>
|
||||
<path class="grey dash" d="M669.90271,302.2623291l2.4954834,32.0210571"/>
|
||||
<path class="dot" d="M663.1259766,301.502655l-10.3438721,33.1818848"/>
|
||||
<path class="grey dash" d="M590.3739624,301.502655l10.3438721,33.1818848"/>
|
||||
<path class="grey dot" d="M583.59729,302.2623291l-2.4954834,32.0210571"/>
|
||||
<path class="grey dash" d="M547.3607788,301.8459167l7.6353149,32.6573486"/>
|
||||
<path class="grey dot" d="M540.4694214,302.1032104l-5.0289307,32.2642212"/>
|
||||
<path class="grey dash" d="M504.2805786,302.1032104l5.0289001,32.2642212"/>
|
||||
<path class="dot" d="M497.3892517,301.8459167l-7.6352539,32.6573486"/>
|
||||
<path class="dash" d="M461.15271,302.2623291l2.4955139,32.0210571"/>
|
||||
<path class="dot" d="M454.3760071,301.502655l-10.3439026,33.1818848"/>
|
||||
<path class="dash" d="M381.6239929,301.502655l10.3439026,33.1818848"/>
|
||||
<path class="grey dot" d="M374.84729,302.2623291l-2.4955139,32.0210571"/>
|
||||
<path class="grey dash" d="M338.6107483,301.8459167l7.6352844,32.6573486"/>
|
||||
<path class="grey dot" d="M331.7193909,302.1032104l-5.0289001,32.2641907"/>
|
||||
<path class="grey dash" d="M295.5306091,302.1032104l5.0289307,32.2642212"/>
|
||||
<path class="dot" d="M288.6392517,301.8459167l-7.6352844,32.6573486"/>
|
||||
<path class="grey dash" d="M252.40271,302.2623291l2.4955139,32.0210571"/>
|
||||
<path class="grey dot" d="M245.6260223,301.502655l-10.3438873,33.1818848"/>
|
||||
<path class="dash" d="M172.8740082,301.502655l10.3438873,33.1818848"/>
|
||||
<path class="grey dot" d="M166.0973053,302.2623291l-2.4955139,32.0210571"/>
|
||||
<path class="grey dash" d="M129.8607635,301.8459167l7.6352844,32.6573486"/>
|
||||
<path class="grey dot" d="M122.9694138,302.1032104l-5.0289078,32.2642212"/>
|
||||
<path class="dash" d="M86.7806091,302.1032104l5.0289078,32.2642212"/>
|
||||
<path class="grey dot" d="M79.8892593,301.8459167l-7.6352768,32.6573486"/>
|
||||
<path class="dash" d="M43.65271,302.2623291l2.4955025,32.0210571"/>
|
||||
<path class="dot" d="M36.8760147,301.502655l-10.3438854,33.1818848"/>
|
||||
<text x="209" y="106">E</text>
|
||||
<text x="626" y="106">T</text>
|
||||
<text x="105" y="166">I</text>
|
||||
<text x="314" y="166">A</text>
|
||||
<text x="522" y="166">N</text>
|
||||
<text x="731" y="166">M</text>
|
||||
<text x="70" y="234">S</text>
|
||||
<text x="140" y="234">U</text>
|
||||
<text x="279" y="234">R</text>
|
||||
<text x="349" y="234">W</text>
|
||||
<text x="488" y="234">D</text>
|
||||
<text x="558" y="234">K</text>
|
||||
<text x="696" y="234">G</text>
|
||||
<text x="766" y="234">O</text>
|
||||
<text x="42" y="294">H</text>
|
||||
<text x="84" y="294">V</text>
|
||||
<text x="126" y="294">F</text>
|
||||
<text x="252" y="294">L</text>
|
||||
<text x="335" y="294">P</text>
|
||||
<text x="376" y="294">J</text>
|
||||
<text x="461" y="294">B</text>
|
||||
<text x="501" y="294">X</text>
|
||||
<text x="543" y="294">C</text>
|
||||
<text x="585" y="294">Y</text>
|
||||
<text x="669" y="294">Z</text>
|
||||
<text x="710" y="292">Q</text>
|
||||
<text x="24" y="351" class="tiny">5</text>
|
||||
<text x="46" y="351" class="tiny">4</text>
|
||||
<text x="94" y="351" class="tiny">3</text>
|
||||
<text x="187" y="351" class="tiny">2</text>
|
||||
<text x="279" y="350" class="tiny">+</text>
|
||||
<text x="395" y="351" class="tiny">1</text>
|
||||
<text x="441" y="351" class="tiny">6</text>
|
||||
<text x="465" y="350" class="tiny">=</text>
|
||||
<text x="489" y="349" style="font-size: 16px">/</text>
|
||||
<text x="650" y="351" class="tiny">7</text>
|
||||
<text x="743" y="351" class="tiny">8</text>
|
||||
<text x="790" y="351" class="tiny">9</text>
|
||||
<text x="813" y="351" class="tiny">0</text>
|
||||
<g>
|
||||
<circle r="37" cx="418" cy="39" class="big"/>
|
||||
<text x="417" y="48" class="big">start</text>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<line class="dot big" style="stroke-dasharray: 3.5, 8.5" x1="102.2679901" y1="50.8910065" x2="249.2680054" y2="50.8910065"/>
|
||||
<path class="arrowhead" d="M120.2640381,50.9020538l12-12l-42,12l42,12L120.2640381,50.9020538z"/>
|
||||
</g>
|
||||
<text x="177" y="35" class="big">Dot</text>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<line class="dash big" style="stroke-dasharray: 8.55, 9.55" x1="592.7290039" y1="50.8910065" x2="727.7290039" y2="50.8910065"/>
|
||||
<path class="arrowhead" d="M715.7359619,50.9020538l-12,12l42-12l-42-12L715.7359619,50.9020538z"/>
|
||||
</g>
|
||||
<text x="657" y="35" class="big">Dash</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 10 KiB |
1037
morse-code_files/constants.js
Normal file
1037
morse-code_files/constants.js
Normal file
File diff suppressed because it is too large
Load Diff
4
morse-code_files/font-awesome.css
vendored
Normal file
4
morse-code_files/font-awesome.css
vendored
Normal file
File diff suppressed because one or more lines are too long
98
morse-code_files/highlight.css
Normal file
98
morse-code_files/highlight.css
Normal file
@ -0,0 +1,98 @@
|
||||
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
|
||||
|
||||
code {
|
||||
background: #fff;
|
||||
font-size: 13px;
|
||||
/*font-weight: 100;*/
|
||||
padding: 4px;
|
||||
border: none;
|
||||
box-shadow: inset 0 0 0 1px #E4EBF2;
|
||||
border-radius: 4px;
|
||||
overflow: scroll;
|
||||
font-family: 'Menlo', 'Monaco', Courier, monospace;
|
||||
}
|
||||
|
||||
pre code {
|
||||
font-size: 13px;
|
||||
word-wrap: break-word;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
-moz-osx-font-smoothing: auto;
|
||||
padding: 0 16px;
|
||||
display: block;
|
||||
color: #222;
|
||||
background: #fff;
|
||||
border: none;
|
||||
box-shadow: inset 0 0 0 1px #E4EBF2;
|
||||
border-radius: 4px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Tomorrow Comment */
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #8e908c;
|
||||
}
|
||||
|
||||
/* Tomorrow Red */
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-regexp,
|
||||
.hljs-deletion {
|
||||
color: #c82829;
|
||||
}
|
||||
|
||||
/* Tomorrow Orange */
|
||||
.hljs-number,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-params,
|
||||
.hljs-meta,
|
||||
.hljs-link {
|
||||
color: #f5871f;
|
||||
}
|
||||
|
||||
/* Tomorrow Yellow */
|
||||
.hljs-attribute {
|
||||
color: #eab700;
|
||||
}
|
||||
|
||||
/* Tomorrow Green */
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-addition {
|
||||
color: #718c00;
|
||||
}
|
||||
|
||||
/* Tomorrow Blue */
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #4271ae;
|
||||
}
|
||||
|
||||
/* Tomorrow Purple */
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag {
|
||||
color: #8959a8;
|
||||
}
|
||||
|
||||
code.hljs {
|
||||
display: block;
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
3
morse-code_files/highlight.js
Normal file
3
morse-code_files/highlight.js
Normal file
File diff suppressed because one or more lines are too long
4
morse-code_files/jquery-2.js
vendored
Normal file
4
morse-code_files/jquery-2.js
vendored
Normal file
File diff suppressed because one or more lines are too long
36
morse-code_files/main.js
Normal file
36
morse-code_files/main.js
Normal file
@ -0,0 +1,36 @@
|
||||
// To make images retina, add a class "2x" to the img element
|
||||
// and add a <image-name>@2x.png image. Assumes jquery is loaded.
|
||||
|
||||
function isRetina() {
|
||||
var mediaQuery = "(-webkit-min-device-pixel-ratio: 1.5),\
|
||||
(min--moz-device-pixel-ratio: 1.5),\
|
||||
(-o-min-device-pixel-ratio: 3/2),\
|
||||
(min-resolution: 1.5dppx)";
|
||||
|
||||
if (window.devicePixelRatio > 1)
|
||||
return true;
|
||||
|
||||
if (window.matchMedia && window.matchMedia(mediaQuery).matches)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
function retina() {
|
||||
|
||||
if (!isRetina())
|
||||
return;
|
||||
|
||||
$("img.2x").map(function(i, image) {
|
||||
|
||||
var path = $(image).attr("src");
|
||||
|
||||
path = path.replace(".png", "@2x.png");
|
||||
path = path.replace(".jpg", "@2x.jpg");
|
||||
|
||||
$(image).attr("src", path);
|
||||
});
|
||||
};
|
||||
|
||||
$(document).ready(retina);
|
680
morse-code_files/morse-code.js
Normal file
680
morse-code_files/morse-code.js
Normal file
@ -0,0 +1,680 @@
|
||||
|
||||
const FREQUENCY = 440;
|
||||
|
||||
var DOT_TIME = 300;
|
||||
var DASH_TIME = DOT_TIME * 3;
|
||||
var SYMBOL_BREAK = DOT_TIME;
|
||||
var LETTER_BREAK = DOT_TIME * 3;
|
||||
var WORD_BREAK = DOT_TIME * 7;
|
||||
|
||||
|
||||
let note_context;
|
||||
let note_node;
|
||||
let gain_node;
|
||||
|
||||
let audioContextInitialized = false;
|
||||
|
||||
async function initializeAudioContext() {
|
||||
note_context = new AudioContext();
|
||||
await note_context.resume();
|
||||
note_node = note_context.createOscillator();
|
||||
gain_node = note_context.createGain();
|
||||
note_node.frequency.value = FREQUENCY.toFixed(2);
|
||||
gain_node.gain.value = 0;
|
||||
note_node.connect(gain_node);
|
||||
gain_node.connect(note_context.destination);
|
||||
note_node.start();
|
||||
audioContextInitialized = true;
|
||||
}
|
||||
|
||||
function startNotePlaying() {
|
||||
// Pass a start time of 0 so it starts ramping up immediately.
|
||||
gain_node.gain.setTargetAtTime(0.1, 0, 0.001)
|
||||
}
|
||||
|
||||
function stopNotePlaying() {
|
||||
// Pass a start time of 0 so it starts ramping down immediately.
|
||||
gain_node.gain.setTargetAtTime(0, 0, 0.001)
|
||||
}
|
||||
|
||||
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// Global strictly increasing play counter to avoid multiple replays at the same time.
|
||||
var playCounter = 0;
|
||||
|
||||
async function playDash(currentPlayCounter) {
|
||||
if (currentPlayCounter != playCounter) { return; }
|
||||
startNotePlaying();
|
||||
await sleep(DASH_TIME);
|
||||
stopNotePlaying();
|
||||
}
|
||||
|
||||
async function playDot(currentPlayCounter) {
|
||||
if (currentPlayCounter != playCounter) { return; }
|
||||
startNotePlaying();
|
||||
await sleep(DOT_TIME);
|
||||
stopNotePlaying();
|
||||
}
|
||||
|
||||
/**
|
||||
* message is something like '---'
|
||||
*/
|
||||
async function playLetter(letter, currentPlayCounter) {
|
||||
// console.log('playSentence with', playCounter, currentPlayCounter);
|
||||
if (!audioContextInitialized) {
|
||||
initializeAudioContext();
|
||||
}
|
||||
// console.log('playing', letter);
|
||||
for (let i = 0; i < letter.length; i++) {
|
||||
if (currentPlayCounter != playCounter) { return; }
|
||||
if (letter[i] == '-') {
|
||||
await playDash(currentPlayCounter);
|
||||
} else if (letter[i] == '.') {
|
||||
await playDot(currentPlayCounter);
|
||||
}
|
||||
await sleep(SYMBOL_BREAK);
|
||||
}
|
||||
}
|
||||
|
||||
// Word is an array of letters, like ['.', '.-', '-']
|
||||
async function playWord(word, currentPlayCounter) {
|
||||
// console.log('playSentence with', playCounter, currentPlayCounter);
|
||||
for (let i = 0; i < word.length; i++) {
|
||||
if (currentPlayCounter != playCounter) { return; }
|
||||
await playLetter(word[i], currentPlayCounter);
|
||||
await sleep(LETTER_BREAK);
|
||||
}
|
||||
}
|
||||
|
||||
// Sentence is an array of words.
|
||||
// Ex. "dog is good" -> [['-..', '---', '--.'], ['..', '...'], ['--.', '---', '---', '-..']]
|
||||
async function playSentence(sentence, currentPlayCounter) {
|
||||
// console.log('playSentence with', playCounter, currentPlayCounter);
|
||||
if (currentPlayCounter != playCounter) { return; }
|
||||
// Slight pause before it starts
|
||||
// console.log('playSentence with', playCounter, currentPlayCounter);
|
||||
await sleep(LETTER_BREAK);
|
||||
// console.log('playSentence with', playCounter, currentPlayCounter);
|
||||
for (let i = 0; i < sentence.length; i++) {
|
||||
if (currentPlayCounter != playCounter) { return; }
|
||||
await playWord(sentence[i], currentPlayCounter);
|
||||
await sleep(WORD_BREAK);
|
||||
}
|
||||
}
|
||||
|
||||
// asciiChar is something like 'd'
|
||||
// Assumes [a-z0-9]
|
||||
function convertAsciiCharToMorse(asciiChar) {
|
||||
return MORSE_MAP[asciiChar];
|
||||
}
|
||||
|
||||
// asciiWord is something like 'dog'
|
||||
// Assumes [a-z0-9]
|
||||
function convertAsciiWordToMorse(asciiWord) {
|
||||
return asciiWord.split('').map(convertAsciiCharToMorse);
|
||||
}
|
||||
|
||||
// asciiSentence is something like 'dog is good'
|
||||
// Assumes words are separated by spaces.
|
||||
function convertAsciiSentenceToMorse(asciiSentence) {
|
||||
let splitSentence = asciiSentence.toLowerCase().split(' ');
|
||||
return splitSentence.map(convertAsciiWordToMorse);
|
||||
}
|
||||
|
||||
function getRandomLetter() {
|
||||
const randomIndex = Math.floor(Math.random() * 26);
|
||||
return Object.keys(MORSE_MAP)[randomIndex];
|
||||
}
|
||||
|
||||
function getRandomWord() {
|
||||
const randomIndex = Math.floor(Math.random() * EASY_WORDS.length);
|
||||
return EASY_WORDS[randomIndex];
|
||||
}
|
||||
|
||||
function getRandomEasyWords() {
|
||||
const totalLength = Math.floor(Math.random() * 3) + 2;
|
||||
let finalSentence = ''
|
||||
for (let i = 0; i < totalLength; i++) {
|
||||
finalSentence += EASY_WORDS[Math.floor(Math.random() * EASY_WORDS.length)];
|
||||
if (i < totalLength - 1) {
|
||||
finalSentence += ' ';
|
||||
}
|
||||
}
|
||||
return finalSentence;
|
||||
}
|
||||
|
||||
function getRandomWords() {
|
||||
const totalLength = Math.floor(Math.random() * 4) + 2;
|
||||
let finalSentence = ''
|
||||
for (let i = 0; i < totalLength; i++) {
|
||||
finalSentence += ALL_WORDS[Math.floor(Math.random() * ALL_WORDS.length)];
|
||||
if (i < totalLength - 1) {
|
||||
finalSentence += ' ';
|
||||
}
|
||||
}
|
||||
return finalSentence;
|
||||
}
|
||||
|
||||
function updateSpeed() {
|
||||
const useFarnsworth = document.querySelector('input[name="farnsworth"]:checked').value == 'yes';
|
||||
// Get the difficulty and update the constants.
|
||||
const difficulty = document.querySelector('input[name="speed"]:checked').value
|
||||
if (useFarnsworth) {
|
||||
DOT_TIME = 60;
|
||||
DASH_TIME = DOT_TIME * 3;
|
||||
SYMBOL_BREAK = DOT_TIME;
|
||||
let letterBreakMultiplier;
|
||||
switch (difficulty) {
|
||||
case 'easy':
|
||||
letterBreakMultiplier = 24;
|
||||
break;
|
||||
case 'medium':
|
||||
letterBreakMultiplier = 12;
|
||||
break;
|
||||
case 'hard':
|
||||
letterBreakMultiplier = 6;
|
||||
break;
|
||||
default:
|
||||
letterBreakMultiplier = 3;
|
||||
break;
|
||||
}
|
||||
LETTER_BREAK = DOT_TIME * letterBreakMultiplier;
|
||||
WORD_BREAK = DOT_TIME * (letterBreakMultiplier * 2.5);
|
||||
} else {
|
||||
switch (difficulty) {
|
||||
case 'easy':
|
||||
DOT_TIME = 300;
|
||||
break;
|
||||
case 'medium':
|
||||
DOT_TIME = 200;
|
||||
break;
|
||||
case 'hard':
|
||||
DOT_TIME = 100;
|
||||
break;
|
||||
default:
|
||||
DOT_TIME = 60;
|
||||
break;
|
||||
}
|
||||
DASH_TIME = DOT_TIME * 3;
|
||||
SYMBOL_BREAK = DOT_TIME;
|
||||
LETTER_BREAK = DOT_TIME * 3;
|
||||
WORD_BREAK = DOT_TIME * 7;
|
||||
}
|
||||
}
|
||||
|
||||
function getTarget() {
|
||||
let target;
|
||||
// Get the difficulty and assign a random target.
|
||||
const difficulty = document.querySelector('input[name="difficulty"]:checked').value
|
||||
switch (difficulty) {
|
||||
case 'easy':
|
||||
target = getRandomLetter();
|
||||
break;
|
||||
case 'medium':
|
||||
target = getRandomWord();
|
||||
break;
|
||||
case 'hard':
|
||||
target = getRandomEasyWords();
|
||||
break;
|
||||
default:
|
||||
target = getRandomWords();
|
||||
break;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
var startButton = document.getElementById('playGame');
|
||||
|
||||
class ListeningGame {
|
||||
constructor(wordInput, statusElement, submitButton, resetButton) {
|
||||
this.stopped = false;
|
||||
this.messageFound = false;
|
||||
|
||||
// DOM elements we'll interact with
|
||||
this.wordInput = wordInput;
|
||||
this.statusElement = statusElement;
|
||||
this.submitButton = submitButton;
|
||||
this.resetButton = resetButton;
|
||||
|
||||
// Bind listeners to `this`.
|
||||
this.inputListener = this.inputListener.bind(this);
|
||||
this.submit = this.submit.bind(this);
|
||||
this.resetButtonListener = this.resetButtonListener.bind(this);
|
||||
|
||||
// Initialize listeners
|
||||
this.wordInput.addEventListener('keyup', this.inputListener);
|
||||
this.submitButton.addEventListener('click', this.submit);
|
||||
this.resetButton.addEventListener('click', this.resetButtonListener);
|
||||
this.wordInput.removeAttribute('disabled');
|
||||
this.submitButton.removeAttribute('disabled');
|
||||
this.resetButton.removeAttribute('disabled');
|
||||
|
||||
this.target = '';
|
||||
}
|
||||
|
||||
startNewGame() {
|
||||
// Start listening
|
||||
if (!audioContextInitialized) {
|
||||
initializeAudioContext();
|
||||
}
|
||||
// Focus on the word input box.
|
||||
this.wordInput.focus();
|
||||
// Update speed based on difficulty.
|
||||
updateSpeed();
|
||||
// Get a new target
|
||||
this.target = getTarget();
|
||||
// console.log('target: ', this.target);
|
||||
// Reset
|
||||
this.messageFound = false;
|
||||
this.wordInput.value = '';
|
||||
this.statusElement.textContent = 'Waiting...';
|
||||
this.playTarget();
|
||||
}
|
||||
|
||||
submit(event) {
|
||||
event.preventDefault();
|
||||
if (this.messageFound) {
|
||||
// If we found it already, get a new target and play it.
|
||||
this.startNewGame();
|
||||
} else {
|
||||
const enteredWord = this.wordInput.value;
|
||||
// console.log('enteredWord: ', enteredWord, 'target: ', this.target);
|
||||
if (enteredWord.toLowerCase() == this.target) {
|
||||
// If they got the word, show message and switch event listener.
|
||||
this.statusElement.textContent = 'Correct! Press enter to play a new round.';
|
||||
this.messageFound = true;
|
||||
// Extra playCounter increase to stop any current playing.
|
||||
this.stopCurrentPlaying();
|
||||
} else {
|
||||
// If they didn't, clear the text box and let them try again.
|
||||
this.statusElement.textContent = 'Not quite! Try again.';
|
||||
this.replay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopCurrentPlaying() {
|
||||
// console.log('stopping current playing');
|
||||
// Also increase playCounter to stop any current playing. Wastes a count, but we have the bits to spare.
|
||||
playCounter += 1;
|
||||
}
|
||||
|
||||
async replay() {
|
||||
if (this.messageFound) {
|
||||
this.startNewGame();
|
||||
return;
|
||||
}
|
||||
// Stop the current playing, and play the target again.
|
||||
this.stopCurrentPlaying();
|
||||
this.wordInput.focus();
|
||||
this.playTarget();
|
||||
}
|
||||
|
||||
resetButtonListener(event) {
|
||||
event.preventDefault();
|
||||
this.replay();
|
||||
}
|
||||
|
||||
inputListener(event) {
|
||||
if (this.messageFound) {
|
||||
this.startNewGame();
|
||||
return;
|
||||
}
|
||||
if (event.key == 'Control') {
|
||||
this.replay();
|
||||
} else if (event.key == 'Enter') {
|
||||
this.submit(event);
|
||||
}
|
||||
}
|
||||
|
||||
playTarget() {
|
||||
playCounter += 1;
|
||||
// console.log('playing with counter', playCounter)
|
||||
playSentence(convertAsciiSentenceToMorse(this.target), playCounter);
|
||||
}
|
||||
|
||||
stopGame() {
|
||||
this.stopCurrentPlaying();
|
||||
this.wordInput.value = '';
|
||||
this.statusElement.textContent = 'Press Start to begin.';
|
||||
this.wordInput.removeEventListener('keyup', this.inputListener);
|
||||
this.submitButton.removeEventListener('click', this.submit);
|
||||
this.resetButton.removeEventListener('click', this.resetButtonListener);
|
||||
this.wordInput.setAttribute('disabled', 'disabled');
|
||||
this.submitButton.setAttribute('disabled', 'disabled');
|
||||
this.resetButton.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
|
||||
var currentGame;
|
||||
|
||||
var playListeningGameButton = document.getElementById("playListeningGame");
|
||||
|
||||
async function playListeningGame() {
|
||||
// Check if there's a currentGame already. If there is, stop it!
|
||||
if (currentGame) {
|
||||
currentGame.stopGame();
|
||||
}
|
||||
|
||||
const wordInput = document.getElementById('wordInput');
|
||||
const status = document.getElementById('status');
|
||||
const submitButton = document.getElementById('submitButton');
|
||||
const resetButton = document.getElementById('resetButton');
|
||||
|
||||
currentGame = new ListeningGame(wordInput, status, submitButton, resetButton);
|
||||
|
||||
currentGame.startNewGame();
|
||||
|
||||
playListeningGameButton.removeEventListener('click', playListeningGame);
|
||||
playListeningGameButton.addEventListener('click', stopListeningGame);
|
||||
playListeningGameButton.textContent = 'Stop';
|
||||
}
|
||||
|
||||
async function stopListeningGame() {
|
||||
if (currentGame) {
|
||||
// Stop current game
|
||||
currentGame.stopGame();
|
||||
}
|
||||
|
||||
playListeningGameButton.removeEventListener('click', stopListeningGame);
|
||||
playListeningGameButton.addEventListener('click', playListeningGame);
|
||||
playListeningGameButton.textContent = 'Start';
|
||||
}
|
||||
|
||||
playListeningGameButton.addEventListener('click', playListeningGame);
|
||||
|
||||
class InputGame {
|
||||
constructor(signalButton, startOverButton, targetDisplay, inputDisplay, inputStatus) {
|
||||
this.signalButton = signalButton;
|
||||
this.startOverButton = startOverButton;
|
||||
this.targetDisplay = targetDisplay;
|
||||
this.inputDisplay = inputDisplay;
|
||||
this.inputStatus = inputStatus;
|
||||
|
||||
this.target = '';
|
||||
|
||||
// Stack for keeping track of dots and dashes that make up letter
|
||||
this.dotAndDashStack = [];
|
||||
// Stack for keeping track of letters that make up word
|
||||
this.letterStack = [];
|
||||
|
||||
this.lastKeyDownTime = 0;
|
||||
this.lastKeyUpTime = 0;
|
||||
this.letterTimeout = null;
|
||||
this.spaceTimeout = null;
|
||||
|
||||
this.matchFound = false;
|
||||
|
||||
// Keep track of where the spacebar is to prevent duplicate keydowns when you hold it down.
|
||||
this.spacebarDown = false;
|
||||
// Keep track of if the tap key is down or not to prevent weird state issues when restarting game.
|
||||
// this.keyIsDown = false;
|
||||
|
||||
this.documentKeydownListener = this.documentKeydownListener.bind(this);
|
||||
this.documentKeyupListener = this.documentKeyupListener.bind(this);
|
||||
this.buttonMousedownListener = this.buttonMousedownListener.bind(this);
|
||||
this.buttonMouseupListener = this.buttonMouseupListener.bind(this);
|
||||
this.startOver = this.startOver.bind(this);
|
||||
this.handleLetterTimeout = this.handleLetterTimeout.bind(this);
|
||||
this.handleSpaceTimeout = this.handleSpaceTimeout.bind(this);
|
||||
|
||||
document.addEventListener('keydown', this.documentKeydownListener);
|
||||
document.addEventListener('keyup', this.documentKeyupListener);
|
||||
this.signalButton.addEventListener('mousedown', this.buttonMousedownListener);
|
||||
this.signalButton.addEventListener('mouseup', this.buttonMouseupListener);
|
||||
this.signalButton.addEventListener('touchstart', this.buttonMousedownListener);
|
||||
this.signalButton.addEventListener('touchend', this.buttonMouseupListener);
|
||||
this.startOverButton.addEventListener('click', this.startOver);
|
||||
}
|
||||
|
||||
startNewGame() {
|
||||
this.matchFound = false;
|
||||
this.startOver();
|
||||
// Update speed based on difficulty.
|
||||
updateSpeed();
|
||||
// Get target word
|
||||
this.target = getTarget();
|
||||
// console.log('target: ', this.target);
|
||||
// Update target display with the new target
|
||||
this.targetDisplay.textContent = 'Target: ' + this.target;
|
||||
this.inputDisplay.textContent = '';
|
||||
this.inputStatus.textContent = '';
|
||||
|
||||
this.signalButton.removeAttribute('disabled');
|
||||
this.startOverButton.removeAttribute('disabled');
|
||||
// Focus on the tap button
|
||||
this.signalButton.focus();
|
||||
|
||||
// Start listening
|
||||
if (!audioContextInitialized) {
|
||||
initializeAudioContext();
|
||||
}
|
||||
}
|
||||
|
||||
keyDown() {
|
||||
if (this.matchFound) {
|
||||
// console.log('match found! doing nothing on keydown')
|
||||
return;
|
||||
}
|
||||
startNotePlaying();
|
||||
const currentTime = Date.now();
|
||||
// Update the marker for the last signal ending
|
||||
this.lastKeyDownTime = currentTime;
|
||||
// Get the total silence time since we last had a signal
|
||||
const silenceDelta = currentTime - this.lastKeyUpTime;
|
||||
this.handleSilence(silenceDelta);
|
||||
// We also want to clear any pending intervals so we don't duplicate them.
|
||||
clearTimeout(this.letterTimeout);
|
||||
clearTimeout(this.spaceTimeout);
|
||||
}
|
||||
|
||||
keyUp() {
|
||||
if (this.matchFound) {
|
||||
this.startNewGame();
|
||||
return;
|
||||
}
|
||||
// console.log('keyup!');
|
||||
stopNotePlaying();
|
||||
const currentTime = Date.now();
|
||||
// Update the marker for the last silence ending
|
||||
this.lastKeyUpTime = currentTime;
|
||||
// Get the total signal time since we last had silence
|
||||
const signalDelta = currentTime - this.lastKeyDownTime;
|
||||
this.handleSignal(signalDelta);
|
||||
|
||||
// TODO: start timer
|
||||
this.letterTimeout = setTimeout(this.handleLetterTimeout, LETTER_BREAK);
|
||||
this.spaceTimeout = setTimeout(this.handleSpaceTimeout, WORD_BREAK);
|
||||
}
|
||||
|
||||
handleLetterTimeout() {
|
||||
this.handleSilence(LETTER_BREAK);
|
||||
}
|
||||
|
||||
handleSpaceTimeout() {
|
||||
this.handleSilence(WORD_BREAK)
|
||||
}
|
||||
|
||||
handleSignal(signalDelta) {
|
||||
// console.log('signalDelta: ', signalDelta)
|
||||
// See if it's a dot or a dash
|
||||
if (signalDelta < DASH_TIME) {
|
||||
this.dotAndDashStack.push('.');
|
||||
} else {
|
||||
this.dotAndDashStack.push('-');
|
||||
}
|
||||
}
|
||||
|
||||
handleSilence(silenceDelta) {
|
||||
// If there's no input yet, don't do anything
|
||||
if (this.dotAndDashStack.length == 0 && this.letterStack.length == 0) {
|
||||
return;
|
||||
}
|
||||
// console.log('silenceDelta: ', silenceDelta, 'LETTER_BREAK: ', LETTER_BREAK);
|
||||
// If we have at least a letter break, try to add a new character if we have any dots or dashes
|
||||
if (silenceDelta >= LETTER_BREAK && this.dotAndDashStack.length != 0) {
|
||||
// TODO: handle letter being done.
|
||||
const currentLetterCombo = this.dotAndDashStack.join('');
|
||||
if (currentLetterCombo in REVERSE_MORSE_MAP) {
|
||||
this.letterStack.push(REVERSE_MORSE_MAP[currentLetterCombo]);
|
||||
} else {
|
||||
// Push a question mark
|
||||
this.letterStack.push('?');
|
||||
}
|
||||
// Clear current stack.
|
||||
this.dotAndDashStack = [];
|
||||
}
|
||||
// If we have more than a word break, we may also want to add a space after doing the above.
|
||||
// We may want to do this without dots and dashes (e.g. the timeout), but only if the last char
|
||||
// isn't a space
|
||||
if (silenceDelta >= WORD_BREAK &&
|
||||
this.letterStack.length != 0 &&
|
||||
this.letterStack[this.letterStack.length - 1] != ' ') {
|
||||
this.letterStack.push(' ');
|
||||
}
|
||||
// Update the display.
|
||||
this.inputDisplay.textContent = this.letterStack.join('');
|
||||
this.checkForMatch();
|
||||
}
|
||||
|
||||
documentKeydownListener(event) {
|
||||
event.preventDefault();
|
||||
// console.log('document keydown');
|
||||
if (event.key == ' ' && !this.spacebarDown) {
|
||||
// console.log('sending keydown');
|
||||
this.spacebarDown = true;
|
||||
this.keyDown();
|
||||
} else if (event.key == 'Control') {
|
||||
this.startOver();
|
||||
}
|
||||
}
|
||||
|
||||
documentKeyupListener(event) {
|
||||
event.preventDefault();
|
||||
if (this.matchFound) {
|
||||
this.spacebarDown = false;
|
||||
this.startNewGame();
|
||||
return;
|
||||
}
|
||||
// console.log('document keyup');
|
||||
// console.log(this);
|
||||
if (event.key == ' ') {
|
||||
this.spacebarDown = false;
|
||||
this.keyUp();
|
||||
}
|
||||
}
|
||||
|
||||
buttonMousedownListener(event) {
|
||||
event.preventDefault();
|
||||
// console.log('button mousedown');
|
||||
// console.log(this);
|
||||
this.keyDown();
|
||||
}
|
||||
|
||||
buttonMouseupListener(event) {
|
||||
event.preventDefault();
|
||||
// console.log('button mouseup');
|
||||
// console.log(this);
|
||||
this.keyUp();
|
||||
}
|
||||
|
||||
startOver(event) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (this.matchFound) {
|
||||
this.startNewGame();
|
||||
return;
|
||||
}
|
||||
this.inputDisplay.textContent = '';
|
||||
this.inputStatus.textContent = '';
|
||||
this.dotAndDashStack = [];
|
||||
this.letterStack = [];
|
||||
this.lastKeyDownTime = 0;
|
||||
this.lastKeyUpTime = 0;
|
||||
|
||||
clearTimeout(this.letterTimeout);
|
||||
clearTimeout(this.spaceTimeout);
|
||||
}
|
||||
|
||||
checkForMatch() {
|
||||
// console.log('checking for match');
|
||||
// console.log('target: \'' + this.target + '\'');
|
||||
// console.log('this.letterStack.join(\'\'): \'' + this.letterStack.join('') + '\'');
|
||||
// console.log(this.letterStack.join('') == this.target);
|
||||
if (this.letterStack.join('') == this.target) {
|
||||
// console.log('match!');
|
||||
// Clear intervals
|
||||
clearTimeout(this.letterTimeout);
|
||||
clearTimeout(this.spaceTimeout);
|
||||
|
||||
// Show message
|
||||
this.inputStatus.textContent = 'You got it! Press any key or click either button to start a new round.';
|
||||
this.matchFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
stopGame() {
|
||||
this.startOver();
|
||||
this.targetDisplay.textContent = 'Press Start to begin.';
|
||||
this.inputStatus.textContent = '';
|
||||
document.removeEventListener('keydown', this.documentKeydownListener);
|
||||
document.removeEventListener('keyup', this.documentKeyupListener);
|
||||
this.signalButton.removeEventListener('mousedown', this.buttonMousedownListener);
|
||||
this.signalButton.removeEventListener('mouseup', this.buttonMouseupListener);
|
||||
this.signalButton.removeEventListener('touchstart', this.buttonMousedownListener);
|
||||
this.signalButton.removeEventListener('touchend', this.buttonMouseupListener);
|
||||
this.startOverButton.removeEventListener('click', this.startOver);
|
||||
this.signalButton.setAttribute('disabled', 'disabled');
|
||||
this.startOverButton.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
|
||||
var playInputGameButton = document.getElementById("playInputGame");
|
||||
|
||||
async function playInputGame() {
|
||||
// TODO: check if there's a currentGame already. If there is, stop it!
|
||||
if (currentGame) {
|
||||
currentGame.stopGame();
|
||||
}
|
||||
|
||||
const signalButton = document.getElementById('signalButton');
|
||||
const startOverButton = document.getElementById('startOverButton');
|
||||
const targetDisplay = document.getElementById('targetDisplay');
|
||||
const inputDisplay = document.getElementById('inputDisplay');
|
||||
const inputStatus = document.getElementById('inputStatus');
|
||||
|
||||
currentGame = new InputGame(signalButton, startOverButton, targetDisplay, inputDisplay, inputStatus);
|
||||
|
||||
currentGame.startNewGame();
|
||||
|
||||
playInputGameButton.removeEventListener('click', playInputGame);
|
||||
playInputGameButton.addEventListener('click', stopInputGame);
|
||||
playInputGameButton.textContent = 'Stop';
|
||||
}
|
||||
|
||||
async function stopInputGame() {
|
||||
if (currentGame) {
|
||||
// Stop current game
|
||||
currentGame.stopGame();
|
||||
}
|
||||
|
||||
playInputGameButton.removeEventListener('click', stopInputGame);
|
||||
playInputGameButton.addEventListener('click', playInputGame);
|
||||
playInputGameButton.textContent = 'Start';
|
||||
}
|
||||
|
||||
playInputGameButton.addEventListener('click', playInputGame);
|
||||
|
||||
// For iOS, you need a touch event before you can play audio!
|
||||
var initializeAudioOnTouch = function() {
|
||||
if (!audioContextInitialized) {
|
||||
initializeAudioContext();
|
||||
}
|
||||
document.removeEventListener('touchend', initializeAudioOnTouch);
|
||||
}
|
||||
document.addEventListener('touchend', initializeAudioOnTouch);
|
935
morse-code_files/style.css
Normal file
935
morse-code_files/style.css
Normal file
@ -0,0 +1,935 @@
|
||||
/* Reset */
|
||||
|
||||
/* Removed sub from this so that subscript and superscript still do what they should */
|
||||
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
margin: 0;
|
||||
padding: 0
|
||||
}
|
||||
|
||||
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
|
||||
display: block
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1
|
||||
}
|
||||
|
||||
blockquote, q {
|
||||
quotes: none
|
||||
}
|
||||
|
||||
blockquote:before, blockquote:after, q:before, q:after {
|
||||
content: none
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0
|
||||
}
|
||||
|
||||
*, *:before, *:after {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
/* Clearfix */
|
||||
|
||||
.clearfix:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* Icons */
|
||||
|
||||
@font-face {
|
||||
font-family: 'icons';
|
||||
src: url('../fonts/icons.eot');
|
||||
src: url('../fonts/icons.eot#iefix') format('embedded-opentype'), url('../fonts/icons.woff') format('woff'), url('../fonts/icons.ttf') format('truetype'), url('../fonts/icons.svg#icons') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
[class^="icon-"]:before, [class*=" icon-"]:before {
|
||||
font-family: "icons";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
speak: none;
|
||||
display: inline-block;
|
||||
text-decoration: inherit;
|
||||
text-align: center;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.icon-facebook:before {
|
||||
content: '\e802';
|
||||
}
|
||||
|
||||
.icon-facebook-squared:before {
|
||||
content: '\e800';
|
||||
}
|
||||
|
||||
.icon-twitter:before {
|
||||
content: '\e801';
|
||||
}
|
||||
|
||||
.icon-twitter-1:before {
|
||||
content: '\e804';
|
||||
}
|
||||
|
||||
.icon-facebook-circled:before {
|
||||
content: '\e805';
|
||||
}
|
||||
|
||||
.icon-twitter-circled:before {
|
||||
content: '\e806';
|
||||
}
|
||||
|
||||
.icon-facebook-rect:before {
|
||||
content: '\e803';
|
||||
}
|
||||
|
||||
|
||||
/* Spacing */
|
||||
|
||||
.post h1, h3, h4, h5, p, pre {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* We don't want sublists to have additional margins. Looks bad when unnested */
|
||||
#post-body ul, #post-list li, #post-body ol {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
/* Base */
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font: 16px/1 "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
color: #222;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 30px;
|
||||
letter-spacing: -1px;
|
||||
color: #222;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font: italic 19px/1.3em Georgia, serif;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.profile #wrapper {
|
||||
padding: 60px 40px 0px;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.profile #header {
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.profile #avatar {
|
||||
display: inline-block;
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.profile h1 {
|
||||
font-weight: 400;
|
||||
letter-spacing: 0px;
|
||||
font-size: 20px;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.profile h2 {
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
color: #222;
|
||||
margin-top: 10px;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
nav.main-nav {
|
||||
padding: 20px 20px 0;
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, .90);
|
||||
margin: 0 auto;
|
||||
text-align: right;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
nav.main-nav a {
|
||||
top: 8px;
|
||||
right: 6px;
|
||||
padding: 8px 12px;
|
||||
color: #5badf0;
|
||||
font-size: 16px;
|
||||
line-height: 1.35;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
nav.main-nav a.cta {
|
||||
background: #5badf0;
|
||||
color: #fff;
|
||||
margin-left: 12px;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
nav.main-nav {
|
||||
padding: 20px 10px 0 0;
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, .90);
|
||||
margin: 0 auto;
|
||||
text-align: right;
|
||||
z-index: 100;
|
||||
}
|
||||
nav.main-nav a {
|
||||
top: 8px;
|
||||
right: 6px;
|
||||
padding: 8px 8px;
|
||||
color: #5badf0;
|
||||
line-height: 1.35;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 324px) {
|
||||
nav.main-nav a.cta {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 60px 40px 100px 40px;
|
||||
}
|
||||
|
||||
#wrapper.home {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 0px 40px 20px 40px;
|
||||
}
|
||||
|
||||
.home #avatar {
|
||||
float: right;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
|
||||
/* Typography */
|
||||
|
||||
|
||||
/*Accent color*/
|
||||
|
||||
a, #title, #post-list a:hover, #title:hover {
|
||||
text-decoration: none;
|
||||
color: #5badf0;
|
||||
color: #5694f1;
|
||||
}
|
||||
|
||||
p a {
|
||||
color: #5694f1;
|
||||
}
|
||||
|
||||
|
||||
/*Transitions*/
|
||||
|
||||
a, #post-nav a, #post-list a {
|
||||
-webkit-transition: all 0.15s ease;
|
||||
-moz-transition: all 0.15s ease;
|
||||
-ms-transition: all 0.15s ease;
|
||||
-o-transition: all 0.15s ease;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove this so that lists are normal?
|
||||
See the "reading this in the future" footnotes
|
||||
*/
|
||||
/* li {
|
||||
list-style-position: inside;
|
||||
} */
|
||||
|
||||
ul>li {
|
||||
list-style-type: circle;
|
||||
}
|
||||
|
||||
|
||||
/* Line Height */
|
||||
|
||||
#post-body, p {
|
||||
line-height: 1.7;
|
||||
}
|
||||
#post-body p > a {
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
b, strong {
|
||||
font-weight: 500;
|
||||
color: #1E2025;
|
||||
}
|
||||
|
||||
em, i {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#title {
|
||||
display: inline-block;
|
||||
line-height: 100%;
|
||||
font-weight: 500;
|
||||
font-size: 19px;
|
||||
margin: 0;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.description {
|
||||
float: right;
|
||||
font: italic 14px/1.4em Georgia, serif;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.home h1 {
|
||||
font-size: 30px;
|
||||
letter-spacing: -1px;
|
||||
color: #222;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.home h2 {
|
||||
font: italic 19px/1.3em Georgia, serif;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.post header {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.post h1 {
|
||||
margin-bottom: 20px;
|
||||
color: #222;
|
||||
font: 300 32px/1.4em "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.post h2 {
|
||||
margin-bottom: 20px;
|
||||
font: 300 24px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.post h2.headline {
|
||||
font: normal 13px/1.5em "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
margin: -5px 0 0 0;
|
||||
color: #333;
|
||||
font-size: 13px;
|
||||
letter-spacing: 1px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.post h2.headline .tags {
|
||||
font: normal 13px/1.5em "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
margin: -5px 0 40px 0;
|
||||
color: #b2b9be;
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-top: 5px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#post-list h2 {
|
||||
font: normal 17px/1.5em "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
color: #333;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
h3, h4, h5 {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
color: #222;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
p.small {
|
||||
color: #222;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
padding-left: 15px;
|
||||
border-left: 3px solid #eee;
|
||||
}
|
||||
|
||||
hr {
|
||||
display: block;
|
||||
border: none;
|
||||
height: 1px;
|
||||
margin: 40px auto;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
margin: 40px 0;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
th, td {
|
||||
text-align: left;
|
||||
padding-right: 20px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table td, td {
|
||||
border-spacing: none;
|
||||
border-style: solid;
|
||||
padding: 10px 15px;
|
||||
border-width: 1px 0 0 0;
|
||||
}
|
||||
|
||||
tr>td {
|
||||
border-top: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
tr:nth-child(odd)>td {
|
||||
background: #fcfcfc;
|
||||
}
|
||||
|
||||
thead th, th {
|
||||
text-align: left;
|
||||
padding: 10px 15px;
|
||||
height: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
color: #444;
|
||||
border-bottom: 1px solid #dadadc;
|
||||
cursor: default;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
video {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
|
||||
/* Made with Cactus Badge */
|
||||
|
||||
#badge {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
right: 8px;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
|
||||
/*=========================================
|
||||
Post List
|
||||
=========================================== */
|
||||
|
||||
#post-list, #archive-list {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
#post-list li, #archive-list li {
|
||||
list-style-type: none;
|
||||
border: 1px solid #333;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
#post-list li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#post-list a {
|
||||
color: #333;
|
||||
display: block;
|
||||
font: bold 19px/1.0 "Helvetica Neue", helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
#post-list .dates {
|
||||
font: 300 17px/1.8 "Helvetica Neue", helvetica, Arial, sans-serif;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
#post-list-footer {
|
||||
border-top: 1px solid #eee;
|
||||
margin-top: 20px;
|
||||
padding-top: 100px;
|
||||
}
|
||||
|
||||
#archive-link {
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
border-radius: 4px;
|
||||
padding: 3px 10px 6px;
|
||||
box-shadow: 0 0 0 1px hsla(207, 83%, 80%, 1);
|
||||
}
|
||||
|
||||
#archive-link:hover {
|
||||
background: #5694f1;
|
||||
color: #fff;
|
||||
box-shadow: 0 0 0 1px #5694f1;
|
||||
}
|
||||
|
||||
#archive-link span {
|
||||
position: relative;
|
||||
top: 0;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
|
||||
/* Post Page */
|
||||
|
||||
#header {
|
||||
}
|
||||
|
||||
.post {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#post-meta {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
line-height: 1.4;
|
||||
border-top: 1px solid #eee;
|
||||
padding-top: 40px;
|
||||
margin-bottom: 40px;
|
||||
padding-bottom: 40px;
|
||||
margin-top: 40px;
|
||||
color: #444;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#post-meta div span {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#post-meta div span.dark {
|
||||
color: #1E2025;
|
||||
}
|
||||
|
||||
#post-meta div {
|
||||
margin: 0 25px 0 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#sharing {
|
||||
float: right;
|
||||
margin: -2px;
|
||||
}
|
||||
|
||||
#sharing a {
|
||||
font-size: 20px;
|
||||
font-size: 23px;
|
||||
margin-left: 1px;
|
||||
margin-top: 4px;
|
||||
color: #d4d4d4;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#sharing a:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
|
||||
/* Post Navigation */
|
||||
|
||||
#post-nav {
|
||||
text-align: center;
|
||||
padding-top: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
#post-nav span {
|
||||
-webkit-transition: all 0.1s linear;
|
||||
-moz-transition: all 0.1s linear;
|
||||
-ms-transition: all 0.1s linear;
|
||||
-o-transition: all 0.1s linear;
|
||||
transition: all 0.1s linear;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#post-nav span.prev {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#post-nav span.next {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#post-nav span .arrow {
|
||||
position: relative;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
#post-nav span.prev:hover .arrow {
|
||||
left: -4px;
|
||||
}
|
||||
|
||||
#post-nav span.next:hover .arrow {
|
||||
right: -4px;
|
||||
}
|
||||
|
||||
#post-nav span.prev:hover {
|
||||
left: -3px;
|
||||
}
|
||||
|
||||
#post-nav span.next:hover {
|
||||
right: -3px;
|
||||
}
|
||||
|
||||
|
||||
/* Archive */
|
||||
|
||||
h1.archive {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
h2.month {
|
||||
width: 100%;
|
||||
font: bold 13px/1 "Helvetica Neue", helvetica, Arial, sans-serif;
|
||||
text-transform: uppercase;
|
||||
margin-top: 40px;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#archive-list li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#archive-list a {
|
||||
display: block;
|
||||
font: bold 17px/1.7 "Helvetica Neue", helvetica, Arial, sans-serif;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#archive-list .dates {
|
||||
float: right;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
font: 300 17px/1.7 "Helvetica Neue", helvetica, Arial, sans-serif;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
#archive-list li a:hover, #archive-list li:hover .dates {
|
||||
color: #5694f1;
|
||||
}
|
||||
|
||||
#post-meta img.avatar {
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
float: left;
|
||||
border-radius: 50%;
|
||||
margin-top: 3px;
|
||||
margin-right: 20px;
|
||||
box-shadow: 0 0 0 3px #fff, 0 0 0 4px #eee;
|
||||
}
|
||||
|
||||
#post-list.archive.readmore {
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
#post-list.archive.readmore h3 {
|
||||
font: 400 20px "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
#post-list.archive.readmore a {
|
||||
font: 400 16px/1.6 "Helvetica Neue", helvetica, Arial, sans-serif;
|
||||
color: #5694f1;
|
||||
}
|
||||
|
||||
#post-list.archive.readmore a:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
#post-list.archive.readmore .dates {
|
||||
font: 300 16px/1.6 "Helvetica Neue", helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
#disqus_thread, #ds-thread {
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
#sharing a.facebook {
|
||||
background: #4361b3;
|
||||
}
|
||||
|
||||
#sharing a.twitter {
|
||||
background: #4fafed;
|
||||
}
|
||||
|
||||
#sharing a {
|
||||
font-size: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
padding: 6px 10px;
|
||||
border-radius: 4px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
|
||||
/* Media Queries */
|
||||
|
||||
@media screen and (max-width: 540px) {
|
||||
#wrapper {
|
||||
padding: 20px 20px 20px 20px;
|
||||
}
|
||||
#header {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
.post {
|
||||
margin: 40px 0;
|
||||
}
|
||||
#footer {
|
||||
margin-top: 60px;
|
||||
}
|
||||
#post-list, #archive-list {
|
||||
margin-top: 0;
|
||||
}
|
||||
#post-meta {
|
||||
margin-top: 60px;
|
||||
}
|
||||
#title {
|
||||
font-size: 17px;
|
||||
}
|
||||
#post-list .dates {
|
||||
}
|
||||
#post-list-footer {
|
||||
margin-top: 20px;
|
||||
padding-top: 40px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 26px;
|
||||
}
|
||||
.post h2.headline {
|
||||
font-size: 13px;
|
||||
}
|
||||
.post h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
.post h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.archive {
|
||||
margin: 0 0 50px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.archive .post-item {
|
||||
padding: 10px 0px;
|
||||
overflow-x: hidden;
|
||||
white-space:nowrap;
|
||||
}
|
||||
|
||||
.archive .post-time {
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
margin: 0;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.archive .post-time {
|
||||
margin: 5px 0;
|
||||
width: auto;
|
||||
font-size: 13px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.archive .post-link {
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.archive .post-item:hover {
|
||||
color: #5694f1;
|
||||
transition: 0.3s ease-out;
|
||||
}
|
||||
.archive .post-item:hover .post-link {
|
||||
color: #5694f1;
|
||||
}
|
||||
|
||||
.fa.fa-heart:hover {
|
||||
color: #ff3356;
|
||||
transition: 0.7s ease-out;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
/* CUSTOM ADDITIONS */
|
||||
|
||||
#social {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
a.symbol {
|
||||
color: #222;
|
||||
font-size: 2rem;
|
||||
text-decoration: none;
|
||||
margin-right: 0.3rem;
|
||||
}
|
||||
|
||||
a.symbol:hover {
|
||||
color: #BCD4DA;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Table of content
|
||||
*/
|
||||
#toc {
|
||||
line-height: 1em;
|
||||
word-wrap: break-word;
|
||||
padding: 8px;
|
||||
padding-left: 16px;
|
||||
padding-bottom: 14px;
|
||||
margin: 0 0 25px 0;
|
||||
display: block;
|
||||
color: #808080;
|
||||
background: #fff;
|
||||
border: none;
|
||||
box-shadow: inset 0 0 0 1px #E4EBF2;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
#TableOfContents {
|
||||
overflow-x: auto;
|
||||
word-wrap: normal;
|
||||
word-break: break-all;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#TableOfContents ul {
|
||||
list-style-type:none;
|
||||
margin: 0;
|
||||
padding: 0 .5em;
|
||||
}
|
||||
#TableOfContents ul li {
|
||||
list-style-type: none;
|
||||
line-height: 1.7em;
|
||||
}
|
||||
#TableOfContents > ul {
|
||||
padding: 0;
|
||||
}
|
||||
#TableOfContents > ul > li > ul {
|
||||
padding: 0 3em;
|
||||
}
|
||||
#TableOfContents a code{
|
||||
font-family: Menlo, Monaco, Courier;
|
||||
background-color: #fff;
|
||||
font-size: 11px;
|
||||
padding: 2px 3px;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tags page
|
||||
*/
|
||||
|
||||
#wrapper.tags {
|
||||
padding: 0px 40px 20px 40px;
|
||||
}
|
||||
.page-tags {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page-tags .tags {
|
||||
font: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
margin: 10px 15px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.page-tags .tags a {
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.page-tags .tags:hover a {
|
||||
color: #5694f1;
|
||||
}
|
60
morse-code_files/styles.css
Normal file
60
morse-code_files/styles.css
Normal file
@ -0,0 +1,60 @@
|
||||
.game-container {
|
||||
padding: 10px;
|
||||
border-radius: 36px;
|
||||
box-shadow: 0px 11px 20px 4px #aaa;
|
||||
}
|
||||
|
||||
#wordInput {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.start-button {
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
margin: 10px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.interaction-button {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 100%;
|
||||
margin: 10px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.game-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.input {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.display-message {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
min-height: 1.7em;
|
||||
}
|
||||
|
||||
.morse-input {
|
||||
white-space: pre-wrap;
|
||||
border-bottom: 1px solid #aaa;
|
||||
border-radius: 4px;
|
||||
min-height: calc(1.7em + 1px);
|
||||
}
|
||||
|
||||
.button-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
#speed-table {
|
||||
max-width: 100%;
|
||||
overflow: scroll;
|
||||
}
|
Loading…
Reference in New Issue
Block a user