Created
March 25, 2014 12:11
-
-
Save bennadel/9760604 to your computer and use it in GitHub Desktop.
Decoding Morse Code With JavaScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Decoding Morse Code With JavaScript</title> | |
<style type="text/css"> | |
div.output {} | |
div.output p.message { | |
font-size: 20px ; | |
line-height: 30px ; | |
} | |
div.output span.possibleCharacters { | |
background-color: gold ; | |
display: inline-block ; | |
padding: 0px 7px 0px 7px ; | |
} | |
div.alphabet { | |
bottom: 0px ; | |
position: fixed ; | |
} | |
div.alphabet h3 { | |
margin: 0px 0px 20px 0px ; | |
} | |
div.alphabet ul.characters { | |
margin: 0px 0px 0px 0px ; | |
padding: 0px 0px 0px 0px ; | |
} | |
div.alphabet ul.characters li { | |
border: 1px solid #CCCCCC ; | |
display: inline-block ; | |
padding: 10px 0px 10px 0px ; | |
text-align: center ; | |
width: 75px ; | |
} | |
div.alphabet ul.characters li span.character { | |
display: block ; | |
font-size: 14px ; | |
line-height: 18px ; | |
margin: 0px 0px 7px 0px ; | |
} | |
div.alphabet ul.characters li span.sequence { | |
display: block ; | |
font-size: 18px ; | |
line-height: 18px ; | |
} | |
</style> | |
<script type="text/javascript" src="./jquery-1.6.3.js"></script> | |
<script type="text/javascript"> | |
// Create a model for the Morse Code interpreter. | |
var morseCode = (function(){ | |
// Define the duration of the dot in milliseconds. | |
this._dotDuration = 250; | |
// Define the duration of the dash in milliseconds. The | |
// dash is supposed to be 3x that of the dot. | |
this._dashDuration = (this._dotDuration * 3); | |
// Define the pause duration. This is the time between | |
// letters and is supposed to be 1x that of the dot. | |
this._pauseDuration = (this._dotDuration * 1); | |
// Define the pattern map for the morse code patterns | |
// as the relate the alpha-numeric characters that they | |
// represent. | |
this._patternMap = { | |
".-": "A", | |
"-...": "B", | |
"-.-.": "C", | |
"-..": "D", | |
".": "E", | |
"..-.": "F", | |
"--.": "G", | |
"....": "H", | |
"..": "I", | |
".---": "J", | |
"-.-": "K", | |
".-..": "L", | |
"--": "M", | |
"-.": "N", | |
"---": "O", | |
".--.": "P", | |
"--.-": "Q", | |
".-.": "R", | |
"...": "S", | |
"-": "T", | |
"..-": "U", | |
"...-": "V", | |
".--": "W", | |
"-..-": "X", | |
"-.--": "Y", | |
"--..": "Z", | |
"-----": "0", | |
".----": "1", | |
"..---": "2", | |
"...--": "3", | |
"....-": "4", | |
".....": "5", | |
"-....": "6", | |
"--...": "7", | |
"---..": "8", | |
"----.": "9" | |
}; | |
// I am the current, transient sequence being evaluated. | |
this._sequence = ""; | |
// ---------------------------------------------- // | |
// ---------------------------------------------- // | |
// I add the given value to the current sequence. | |
// | |
// Throws InvalidTone if not a dot or dash. | |
this.addSequence = function( value ){ | |
// Check to make sure the value is valid. | |
if ( | |
(value !== ".") && | |
(value !== "-") | |
){ | |
// Invalid value. | |
throw( new Error( "InvalidTone" ) ); | |
} | |
// Add the given value to the end of the current | |
// sequence value. | |
this._sequence += value; | |
// Return this object reference. | |
return( this ); | |
}; | |
// I add a dash to the current sequence. | |
this.dash = function(){ | |
// Reroute to the addSequence(); | |
return( this.addSequence( "-" ) ); | |
}; | |
// I add a dot to the current sequence. | |
this.dot = function(){ | |
// Reroute to the addSequence(); | |
return( this.addSequence( "." ) ); | |
}; | |
// I get the alpha-numeric character set as an array of | |
// sequence-character pairs. | |
this.getAlphabet = function(){ | |
// Create the empty set. | |
var characterSet = []; | |
// Loop over the patterns to map them to a character | |
// set item. | |
for (var pattern in this._patternMap){ | |
// Push it onto the set. | |
characterSet.push({ | |
sequence: pattern, | |
character: this._patternMap[ pattern ] | |
}); | |
} | |
// Sort the character set alphabetically. | |
characterSet.sort( | |
function( a, b ){ | |
return( a.character <= b.character ? -1 : 1 ); | |
} | |
); | |
// Return the character set. | |
return( characterSet ); | |
}; | |
// I get the dash duration. | |
this.getDashDuration = function(){ | |
return( this._dashDuration ); | |
}; | |
// I get the dot duration. | |
this.getDotDuration = function(){ | |
return( this._dotDuration ); | |
}; | |
// I get the pause duration. | |
this.getPauseDuration = function(){ | |
return( this._pauseDuration ); | |
}; | |
// I reset the current sequence. | |
this.resetSequence = function(){ | |
// Clear the sequence. | |
this._sequence = ""; | |
}; | |
// I get the possible character matches based on the | |
// current sequence. | |
this.resolvePartial = function(){ | |
// Create an array to hold our possible characters. | |
var potentialCharacters = []; | |
// Loop over the pattern match to find partial matches. | |
for (var pattern in this._patternMap){ | |
// Check to see if the current sequence can be | |
// the start of the given pattern. | |
if (pattern.indexOf( this._sequence ) === 0){ | |
// Add this character to the list. | |
potentialCharacters.push( | |
this._patternMap[ pattern ] | |
); | |
} | |
} | |
// Return the potential character matches. | |
return( potentialCharacters.sort() ); | |
}; | |
// I get the alpha-numeric charater repsented by the | |
// current sequence. I also also reset the internal | |
// sequence value. | |
// | |
// Throws InvalidSequence if it cannot be mapped to a | |
// valid alpha-numeric character. | |
this.resolveSequence = function(){ | |
// Check to see if the current sequence is valid. | |
if (!this._patternMap.hasOwnProperty( this._sequence )){ | |
// The sequence cannot be matched. | |
throw( new Error( "InvalidSequence" ) ); | |
} | |
// Get the alpha-numeric mapping. | |
var character = this._patternMap[ this._sequence ]; | |
// Reset the sequence. | |
this._sequence = ""; | |
// Return the mapped character. | |
return( character ); | |
}; | |
// ---------------------------------------------- // | |
// ---------------------------------------------- // | |
// Return this object reference. | |
return( this ); | |
}).call( {} ); | |
</script> | |
</head> | |
<body> | |
<h1> | |
Decoding Morse Code With JavaScript | |
</h1> | |
<!-- BEGIN: Output. --> | |
<div class="output"> | |
<h3> | |
Your Message (Press any key to interact): | |
</h3> | |
<p class="message"> | |
<span class="characters"> | |
<!-- This will be populated by the user. --> | |
</span> | |
<span class="possibleCharacters"> | |
<!-- This will be populated dynamically. --> | |
</span> | |
</p> | |
</div> | |
<!-- END: Output. --> | |
<!-- BEGIN: Alphabet. --> | |
<div class="alphabet"> | |
<h3> | |
Morse Code Alphabet | |
</h3> | |
<ul class="characters"> | |
<!-- This will be populated dynamically. --> | |
</ul> | |
<!-- Define the template. ---> | |
<script type="application/x-template" class="template"> | |
<li> | |
<span class="character"></span> | |
<span class="sequence"></span> | |
</li> | |
</script> | |
</div> | |
<!-- END: Alphabet. --> | |
<script type="text/javascript"> | |
// Initialize the alphabet display. | |
(function( $, container, morseCode ){ | |
// Get the dom elements in this module. | |
var dom = { | |
characters: container.find( "ul.characters" ), | |
template: container.find( "script.template" ) | |
}; | |
// Get the the alphabet from the morse code model. | |
var alphabet = morseCode.getAlphabet(); | |
// Loop over the alphabet to build the output. | |
for (var i = 0 ; i < alphabet.length ; i++){ | |
// Get the current letter short-hand. | |
var letter = alphabet[ i ]; | |
// Create a new instance of the template. | |
var template = $( dom.template.html() ); | |
// Set the character. | |
template.find( "span.character" ).text( | |
letter.character | |
); | |
// Set the sequence. | |
template.find( "span.sequence" ).text( | |
letter.sequence | |
); | |
// Add the template to the output. | |
dom.characters.append( template ); | |
} | |
})( jQuery, $( "div.alphabet" ), morseCode ); | |
// -------------------------------------------------- // | |
// -------------------------------------------------- // | |
// Initialize the interpreter. | |
(function( $, container, morseCode ){ | |
// Get the dom elements in this module. | |
var dom = { | |
characters: container.find( "span.characters" ), | |
possibleCharacters: container.find( "span.possibleCharacters" ) | |
}; | |
// Get the dot and dash durations (in milliseconds). | |
var dotDuration = morseCode.getDotDuration(); | |
var dashDuration = morseCode.getDashDuration(); | |
var pauseDuration = morseCode.getPauseDuration(); | |
// Store the date/time for the keydown. | |
var keyDownDate = null; | |
// Keep a timer for post-key resolution for characters. | |
var resolveTimer = null; | |
// Keep a timer for adding a new space to the message. | |
var spaceTimer = null; | |
// For this module, we are going to bind to any key click | |
// to indicate an interaction with the interpreter. There | |
// will be no other key interaction. | |
$( document ).keydown( | |
function( event ){ | |
// Prevent any default action. | |
event.preventDefault(); | |
// Check to see if there is a key-down date. If | |
// so, then exit - we only want the first press | |
// event to be registered. | |
if (keyDownDate){ | |
// Don't process this event. | |
return; | |
} | |
// Clear the resolution timer. | |
clearTimeout( resolveTimer ); | |
// Clear the space timer. | |
clearTimeout( spaceTimer ); | |
// Store the date for this key-down. | |
keyDownDate = new Date(); | |
} | |
); | |
$( document ).keyup( | |
function( event ){ | |
// Prevent any default action. | |
event.preventDefault(); | |
// Determine the keypress duration. | |
var keyPressDuration = ((new Date())- keyDownDate); | |
// Clear the key down date so subsequent key | |
// press events can be processed. | |
keyDownDate = null; | |
// Check to see if the duration indicates a dot | |
// or a dash. | |
if (keyPressDuration <= dotDuration){ | |
// Push a dot. | |
morseCode.dot(); | |
} else { | |
// Push a dash. | |
morseCode.dash(); | |
} | |
// Display the possible characters for the current | |
// sequence. | |
dom.possibleCharacters.text( | |
morseCode.resolvePartial().join( " , " ) | |
); | |
// Now that the key has been pressed, we need to | |
// wait a bit to see if we need to resolve the | |
// current sequence (if the user doesn't interact | |
// with the interpreter, we'll resolve). | |
resolveTimer = setTimeout( | |
function(){ | |
// Try to resolve the sequence. | |
try { | |
// Get the character respresented by | |
// the current sequence. | |
var character = morseCode.resolveSequence(); | |
// Add it to the output. | |
dom.characters.text( | |
dom.characters.text() + character | |
); | |
} catch (e) { | |
// Reset the sequence - something | |
// went wrong with the user's input. | |
morseCode.resetSequence(); | |
} | |
// Clear the possible matches. | |
dom.possibleCharacters.empty(); | |
// Set a timer to add a new space to the | |
// message. | |
spaceTimer = setTimeout( | |
function(){ | |
// Add a "space". | |
dom.characters.text( | |
dom.characters.text() + "__" | |
); | |
}, | |
3500 | |
); | |
}, | |
(pauseDuration * 3) | |
); | |
} | |
); | |
})( jQuery, $( "div.output" ), morseCode ); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice code