(function (root) {
'use strict';
mor.js 1.1.0
(c) 2014 Alasdair Mercer
Freely distributable under the MIT license.
For all details and documentation:
(function (root) {
'use strict';
Pattern placeholder for long marks.
var LONG = 'L';
Pattern placeholder for short marks.
var SHORT = 'S';
Save the previous value of the morjs
variable.
var previousMorjs = root.morjs;
Iterator over a given list
and yield each element in turn to an iterator
function.
var each = function(list, iterator) {
if (!list) {
return;
}
var length = list.length;
for (var index = 0; index < length; index++) {
iterator(list[index], index, list);
}
};
Return the character mapped to the specified pattern
where possible.
var getCharacter = function(pattern) {
pattern = pattern.toLocaleUpperCase();
var character;
for (var key in chars) {
if (chars.hasOwnProperty(key) && chars[key] === pattern) {
character = key;
break;
}
}
return character;
};
Return the mode mapping that matches the name provided.
var getMode = function(name) {
return modes[name];
};
Return the given options
with all of the defaults
applied.
var getOptions = function(options, defaults) {
options = options || {};
for (var key in defaults) {
if (typeof options[key] === 'undefined') {
options[key] = defaults[key];
}
}
return options;
};
Return the pattern mapped to the specified character
where possible.
var getPattern = function(character) {
return chars[character.toLocaleUpperCase()];
};
Parse a string by replacing any instances of the queries provided with their corresponding replacement strings.
var parse = function(str, query1, replacement1, query2, replacement2, spacer) {
var hasSpacer = typeof spacer === 'string';
var rQuery = new RegExp('(' + query1 + '|' + query2 + ')', 'g');
var result = '';
each(str.match(rQuery), function(substr, i) {
Insert spacer if provided and not first loop.
if (hasSpacer && i > 0) {
result += spacer;
}
Swap queries with their replacements while maintaining new lines.
if (substr === query1) {
result += replacement1;
} else if (substr === query2) {
result += replacement2;
}
});
return result;
};
Prepare the string to simplify encoding/decoding. The return value is a multi-dimensional array and should be treated as such.
var prepare = function(str, wordSplitter, letterSplitter, charSplitter) {
if (typeof str !== 'string') {
throw new TypeError('Invalid value type: ' + typeof str);
}
var hasCharSplitter = typeof charSplitter !== 'undefined';
var rWordSplitter = new RegExp(wordSplitter + '|[\\n\\r]+', 'g');
var result = str.trim().split(rWordSplitter);
each(result, function(word, i) {
result[i] = word = word.split(letterSplitter);
if (hasCharSplitter) {
each(word, function(ch, j) {
result[i][j] = ch.split(charSplitter).join('');
});
}
});
return result;
};
Repeat a string the specified number of times
.
var repeat = function(str, times) {
if (!str) {
return str;
}
for (var index = 0, result = ''; index < times; index++) {
result += str;
}
return result;
};
Map of Morse code patterns to supported characters.
var chars = {
/* Letters */
'\u0041': 'SL', /* A */
'\u0042': 'LSSS', /* B */
'\u0043': 'LSLS', /* C */
'\u0044': 'LSS', /* D */
'\u0045': 'S', /* E */
'\u0046': 'SSLS', /* F */
'\u0047': 'LLS', /* G */
'\u0048': 'SSSS', /* H */
'\u0049': 'SS', /* I */
'\u004A': 'SLLL', /* J */
'\u004B': 'LSL', /* K */
'\u004C': 'SLSS', /* L */
'\u004D': 'LL', /* M */
'\u004E': 'LS', /* N */
'\u004F': 'LLL', /* O */
'\u0050': 'SLLS', /* P */
'\u0051': 'LLSL', /* Q */
'\u0052': 'SLS', /* R */
'\u0053': 'SSS', /* S */
'\u0054': 'L', /* T */
'\u0055': 'SSL', /* U */
'\u0056': 'SSSL', /* V */
'\u0057': 'SLL', /* W */
'\u0058': 'LSSL', /* X */
'\u0059': 'LSLL', /* Y */
'\u005A': 'LLSS', /* Z */
/* Numbers */
'\u0030': 'LLLLL', /* 0 */
'\u0031': 'SLLLL', /* 1 */
'\u0032': 'SSLLL', /* 2 */
'\u0033': 'SSSLL', /* 3 */
'\u0034': 'SSSSL', /* 4 */
'\u0035': 'SSSSS', /* 5 */
'\u0036': 'LSSSS', /* 6 */
'\u0037': 'LLSSS', /* 7 */
'\u0038': 'LLLSS', /* 8 */
'\u0039': 'LLLLS', /* 9 */
/* Punctuation */
'\u002E': 'SLSLSL', /* Full stop */
'\u002C': 'LLSSLL', /* Comma */
'\u003F': 'SSLLSS', /* Question mark */
'\u0027': 'SLLLLS', /* Apostrophe */
'\u0021': 'LSLSLL', /* Exclamation mark */
'\u002F': 'LSSLS', /* Slash */
'\u0028': 'LSLLS', /* Left parenthesis */
'\u0029': 'LSLLSL', /* Right parenthesis */
'\u0026': 'SLSSS', /* Ampersand */
'\u003A': 'LLLSSS', /* Colon */
'\u003B': 'LSLSLS', /* Semicolon */
'\u003D': 'LSSSL', /* Equal sign */
'\u002B': 'SLSLS', /* Plus sign */
'\u002D': 'LSSSSL', /* Hyphen-minus */
'\u005F': 'SSLLSL', /* Low line */
'\u0022': 'SLSSLS', /* Quotation mark */
'\u0024': 'SSSLSSL', /* Dollar sign */
'\u0040': 'SLLSLS', /* At sign */
/* Non-English extensions */
'\u00C4': 'SLSL', /* A with diaeresis */
'\u00C6': 'SLSL', /* A and E as grapheme */
'\u0104': 'SLSL', /* A with ogonek */
'\u00C0': 'SLLSL', /* A with grave */
'\u00C5': 'SLLSL', /* A with ring above */
'\u00C7': 'LSLSS', /* C with cedilla */
'\u0108': 'LSLSS', /* C with circumflex */
'\u0106': 'LSLSS', /* C with acute */
'\u0160': 'LLLL', /* S with caron */
'\u00D0': 'SSLLS', /* Eth */
'\u015A': 'SSSLSSS', /* S with acute */
'\u00C8': 'SLSSL', /* E with grave */
'\u0141': 'SLSSL', /* L with stroke */
'\u00C9': 'SSLSS', /* E with acute */
'\u0110': 'SSLSS', /* D with stroke */
'\u0118': 'SSLSS', /* E with ogonek */
'\u011C': 'LLSLS', /* G with circumflex */
'\u0124': 'LLLL', /* H with circumflex */
'\u0134': 'SLLLS', /* J with circumflex */
'\u0179': 'LLSSLS', /* Z with acute */
'\u00D1': 'LLSLL', /* N with tilde */
'\u0143': 'LLSLL', /* N with acute */
'\u00D6': 'LLLS', /* O with diaeresis */
'\u00D8': 'LLLS', /* O with stroke */
'\u00D3': 'LLLS', /* O with acute */
'\u015C': 'SSSLS', /* S with circumflex */
'\u00DE': 'SLLSS', /* Thorn */
'\u00DC': 'SSLL', /* U with diaeresis */
'\u016C': 'SSLL', /* U with breve */
'\u017B': 'LLSSL' /* Z with dot above */
};
Map of supported modes used to encode/decode messages.
var modes = {
Classic “dot” and “dash” output.
classic: {
charSpacer: '\u0020', /* Space */
letterSpacer: repeat('\u0020', 3), /* Space (x3) */
longString: '\u002D', /* Hyphen-minus */
shortString: '\u00B7', /* Middle dot */
wordSpacer: repeat('\u0020', 7) /* Space (x7) */
},
Same as the classic mode except outputs xml entities instead of unicode characters.
classicEntities: {
charSpacer: ' ', /* Non-breaking space */
letterSpacer: repeat(' ', 3), /* Non-breaking space (x3) */
longString: '-', /* Hyphen-minus */
shortString: '·', /* Middle dot */
wordSpacer: repeat(' ', 7) /* Non-breaking space (x7) */
},
Compact version of the classic mode with reduced whitespace.
compact: {
charSpacer: '', /* <Empty> */
letterSpacer: '\u0020', /* Space */
longString: '\u002D', /* Hyphen-minus */
shortString: '\u00B7', /* Middle dot */
wordSpacer: repeat('\u0020', 3) /* Space (x3) */
},
Compact version of the classicEntities mode with reduced whitespace.
compactEntities: {
charSpacer: '', /* <Empty> */
letterSpacer: ' ', /* Non-breaking space */
longString: '-', /* Hyphen-minus */
shortString: '·', /* Middle dot */
wordSpacer: repeat(' ', 3) /* Non-breaking space (x3) */
},
Simple output using a plain hyphen and full stop mixed using a single space to split letters and a new line for words.
simple: {
charSpacer: '', /* <Empty> */
letterSpacer: '\u0020', /* Space */
longString: '\u002D', /* Hyphen-minus */
shortString: '\u002E', /* Full stop */
wordSpacer: repeat('\u0020', 3) /* Space (x3) */
}
};
Build the publicly exposed API.
var morjs = {
Current version of morjs
.
VERSION: '1.1.0',
Expose the available character mappings.
chars: chars,
Default values to be used if no options are specified or are incomplete.
defaults: {
mode: 'compact'
},
Expose the available modes.
modes: modes,
Decode the message
provided from Morse code to a human-readable message.
The message will not be decoded correctly if the mode used to decode the message is not the same as that used to
encode it.
If the mode
option is not specified, the default mode will be used.
decode: function(message, options) {
message = message || '';
options = getOptions(options, morjs.defaults);
var mode = getMode(options.mode);
var result = '';
var value = prepare(message, mode.wordSpacer, mode.letterSpacer, mode.charSpacer);
Ensure message was prepared successfully.
if (!value) {
return result;
}
Iterate over each word.
each(value, function(word, i) {
Insert space between each word.
if (i > 0) {
result += ' ';
}
Iterate over each character of word.
each(word, function(ch) {
var character;
Reverse engineer pattern for character.
var pattern = parse(ch, mode.shortString, SHORT, mode.longString, LONG);
Check if pattern is supported.
if (pattern) {
Retrieve first character matching the pattern.
character = getCharacter(pattern);
Append character if it’s supported.
if (typeof character === 'string') {
result += character;
}
}
});
});
return result;
},
Encode the message
provided in to the Morse code.
If the mode
option is not specified, the default mode will be used.
encode: function(message, options) {
message = message || '';
options = getOptions(options, morjs.defaults);
var mode = getMode(options.mode);
var result = '';
var value = prepare(message, '\\s+', '');
Ensure message was prepared successfully.
if (!value) {
return result;
}
Iterate over each word.
each(value, function(word, i) {
Insert medium gap between each word.
if (i > 0) {
result += mode.wordSpacer;
}
Iterate over each character of word
each(word, function(character, j) {
Insert short gap between each letter.
if (j > 0) {
result += mode.letterSpacer;
}
Retrieve first character matching the character.
var pattern = getPattern(character);
Parse pattern for character if it’s supported.
if (typeof pattern === 'string') {
result += parse(pattern, SHORT, mode.shortString, LONG, mode.longString, mode.charSpacer);
}
});
});
return result;
},
Run mor.js in noConflict mode, returning the morjs
variable to its previous owner.
Returns a reference to morjs
.
noConflict: function() {
root.morjs = previousMorjs;
return this;
}
};
Export morjs
for NodeJS and CommonJS.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = morjs;
}
exports.morjs = morjs;
} else if (typeof define === 'function' && define.amd) {
define(function() {
return morjs;
});
} else {
root.morjs = morjs;
}
})(this);