Add initial files

This commit is contained in:
Nathan McRae 2022-02-28 13:16:41 -08:00
commit 47ccd46ac5
12 changed files with 4721 additions and 0 deletions

315
morse-code.htm Normal file
View 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 its 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 youll 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 youre 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 dont 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 — its 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, Im sorry if its 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 its 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>Im far from an expert, but so far Ive 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>

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 27 KiB

View 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

File diff suppressed because it is too large Load Diff

4
morse-code_files/font-awesome.css vendored Normal file

File diff suppressed because one or more lines are too long

View 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;
}

File diff suppressed because one or more lines are too long

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
View 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);

View 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
View 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;
}

View 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;
}