@ondoher/enigma 1.0.12 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -17
- package/bin/config.js +11 -0
- package/bin/enigma.js +125 -0
- package/bin/enigma.json +17 -0
- package/bin/instructions.txt +1 -0
- package/bin/types.d.ts +21 -0
- package/jsconfig.json +7 -3
- package/lib/enigma/Encoder.js +69 -27
- package/lib/enigma/Enigma.js +72 -19
- package/lib/enigma/EnigmaTypes.d.ts +2 -1
- package/lib/enigma/EntryDisc.js +9 -6
- package/lib/enigma/Inventory.js +11 -11
- package/lib/enigma/PlugBoard.js +11 -8
- package/lib/enigma/Reflector.js +6 -7
- package/lib/enigma/Rotor.js +49 -30
- package/lib/enigma/consts.js +1 -1
- package/lib/enigma/standardInventory.js +3 -3
- package/lib/enigma/tests/EnigmaSpec.js +14 -14
- package/lib/enigma/tests/RotorSpec.js +25 -25
- package/lib/generator/CodeBook.js +26 -7
- package/lib/generator/Generator.js +20 -7
- package/lib/utils/Random.js +12 -12
- package/lib/utils/options.js +63 -0
- package/lib/utils/optionsTypes.d.ts +5 -0
- package/package.json +6 -4
- package/scripts/test.js +5 -5
- package/types/enigma/Encoder.d.ts +80 -39
- package/types/enigma/Enigma.d.ts +28 -12
- package/types/enigma/EntryDisc.d.ts +2 -2
- package/types/enigma/Inventory.d.ts +11 -11
- package/types/enigma/PlugBoard.d.ts +6 -5
- package/types/enigma/Reflector.d.ts +2 -2
- package/types/enigma/Rotor.d.ts +17 -12
- package/types/enigma/consts.d.ts +1 -1
- package/types/enigma/standardInventory.d.ts +1 -1
- package/types/generator/CodeBook.d.ts +34 -15
- package/types/generator/Generator.d.ts +26 -13
- package/types/index.d.ts +2 -4
- package/types/utils/Random.d.ts +12 -12
- package/scripts/hamlet.html +0 -8880
- package/scripts/parseHamlet.js +0 -32
- package/scripts/test-messages.js +0 -60
- package/scripts/x +0 -6446
package/README.md
CHANGED
|
@@ -20,16 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
# Getting Started
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
standard for importing modules, and can present challenges to developers who
|
|
25
|
-
need to mix the two. Using CJS modules in an ESM project is pretty straight
|
|
26
|
-
forward, but the opposite is not true. Here is a good page that covers this
|
|
27
|
-
issue [Mixing ESM and CJS](https://adamcoster.com/blog/commonjs-and-esm-importexport-compatibility-examples).
|
|
28
|
-
To work with this code, your best bet is to create your own project also as ESM,
|
|
29
|
-
by either renaming your files with the extension mjs, or adding
|
|
30
|
-
```"type": "module"``` to your package.js file.
|
|
31
|
-
|
|
32
|
-
To install the module, use the command:
|
|
23
|
+
To install the toolkit, use the command:
|
|
33
24
|
|
|
34
25
|
```npm install @ondoher/enigma```
|
|
35
26
|
|
|
@@ -38,7 +29,7 @@ You can then import this into your code like this:
|
|
|
38
29
|
```JavaScript
|
|
39
30
|
import {Enigma} from '@ondoher/enigma';
|
|
40
31
|
|
|
41
|
-
|
|
32
|
+
let enigma = new Enigma("I", {reflector: 'B'});
|
|
42
33
|
```
|
|
43
34
|
|
|
44
35
|
In addition to this api, there is also [documentation](https://github.com/Ondoher/enigma/blob/main/docs/enigma.md)
|
|
@@ -47,7 +38,6 @@ about its operation. Following that is a detailed breakdown of how to go about
|
|
|
47
38
|
writing a simulation and all of the small details and quirks that will need to
|
|
48
39
|
be accounted for. And there are many.
|
|
49
40
|
|
|
50
|
-
|
|
51
41
|
The API is broken into two main parts, the simulator and the message generator.
|
|
52
42
|
With the simulator you can construct an entire Enigma, or just create instances
|
|
53
43
|
of the individual components. This API is provided as a working reference that
|
|
@@ -411,7 +401,7 @@ encoding the letter.
|
|
|
411
401
|
undefined or the encoded character.
|
|
412
402
|
|
|
413
403
|
---
|
|
414
|
-
`
|
|
404
|
+
`translate(start, text)`
|
|
415
405
|
|
|
416
406
|
Call this method to encode a whole string.
|
|
417
407
|
|
|
@@ -446,16 +436,16 @@ An array of the installed rotors
|
|
|
446
436
|
```javascript
|
|
447
437
|
import {Enigma} from '@ondoher/enigma';
|
|
448
438
|
|
|
449
|
-
|
|
439
|
+
let enigma = new Enigma("I", {reflector: 'B'});
|
|
450
440
|
|
|
451
441
|
enigma.configure({
|
|
452
442
|
rotors: ['III', 'VI', 'VIII'],
|
|
453
|
-
|
|
443
|
+
ringSettings: [1, 8, 13],
|
|
454
444
|
plugs: 'AN EZ HK IJ LR MQ OT PV SW UX'
|
|
455
445
|
});
|
|
456
446
|
|
|
457
|
-
|
|
458
|
-
|
|
447
|
+
let message = 'YKAENZAPMSCHZBFOCUVMRMDPYCOFHADZIZMEFXTHFLOLPZLFGGBOTGOXGRETDWTJIQHLMXVJWKZUASTR'
|
|
448
|
+
let decoded = enigma.translate('UZV', message)
|
|
459
449
|
|
|
460
450
|
console.log(decoded)
|
|
461
451
|
|
package/bin/config.js
ADDED
package/bin/enigma.js
CHANGED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import parseArgs from 'minimist';
|
|
2
|
+
import { getOptions } from '../lib/utils/options.js';
|
|
3
|
+
import { access, constants, readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { CONFIG } from './config.js';
|
|
5
|
+
import Enigma from 'enigma/Enigma';
|
|
6
|
+
|
|
7
|
+
let argv = parseArgs(process.argv.slice(2));
|
|
8
|
+
let params = argv._;
|
|
9
|
+
let instructionText = '';
|
|
10
|
+
|
|
11
|
+
/** @type {Options} */
|
|
12
|
+
let defaultOptions = {
|
|
13
|
+
file: './enigma.json',
|
|
14
|
+
overwrite: false,
|
|
15
|
+
events: '',
|
|
16
|
+
step: false
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** @type {OptionsNameMap} */
|
|
20
|
+
let nameMap = {
|
|
21
|
+
file: 'f',
|
|
22
|
+
overwrite: 'o',
|
|
23
|
+
events: 'e',
|
|
24
|
+
step: 's'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
/** @type {Options} */
|
|
29
|
+
let options = getOptions(defaultOptions, argv, nameMap);
|
|
30
|
+
let command = params[0];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Call this function to check if a file exists
|
|
34
|
+
*
|
|
35
|
+
* @param {String} name
|
|
36
|
+
* @returns {Promise<boolean>}
|
|
37
|
+
*/
|
|
38
|
+
async function fileExists(name) {
|
|
39
|
+
try {
|
|
40
|
+
await access(name, constants.R_OK | constants.W_OK);
|
|
41
|
+
return true;
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Call this method to see if the config file exists
|
|
49
|
+
* @returns
|
|
50
|
+
*/
|
|
51
|
+
async function configExists() {
|
|
52
|
+
return await fileExists(options.file)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
*
|
|
57
|
+
* @returns {Promise<EnigmaConfig>}
|
|
58
|
+
*/
|
|
59
|
+
async function readConfig() {
|
|
60
|
+
if (!configExists) {
|
|
61
|
+
return CONFIG;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return JSON.parse(await readFile( options.file, 'utf-8'));
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function instructions() {
|
|
69
|
+
console.log(instructionText);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
*
|
|
74
|
+
* @param {string} str
|
|
75
|
+
*/
|
|
76
|
+
function error(str) {
|
|
77
|
+
console.log(str);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function init() {
|
|
81
|
+
if (await configExists() && !options.overwrite) {
|
|
82
|
+
error("cannot overwrite config file")
|
|
83
|
+
instructions();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await writeFile(options.file, JSON.stringify(CONFIG, null, " "), 'utf-8');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function runEnigma() {
|
|
91
|
+
let config = await readConfig();
|
|
92
|
+
if (params.length > 1) {
|
|
93
|
+
config.encode = params[1];
|
|
94
|
+
}
|
|
95
|
+
let enigmaConfig = config.enigma;
|
|
96
|
+
if (!config.encode) {
|
|
97
|
+
error('no string to encode');
|
|
98
|
+
instructions();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
let { name, rotors, ringSettings, reflector, plugs} = enigmaConfig;
|
|
102
|
+
let enigma = new Enigma(name, {reflector});
|
|
103
|
+
enigma.configure({rotors, ringSettings, plugs})
|
|
104
|
+
let decoded = enigma.translate("", config.encode)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function main() {
|
|
108
|
+
instructionText = await readFile('./instructions.txt', 'utf-8');
|
|
109
|
+
if (!command) {
|
|
110
|
+
instructions();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
switch (command) {
|
|
115
|
+
case "init":
|
|
116
|
+
await init();
|
|
117
|
+
break;
|
|
118
|
+
|
|
119
|
+
default:
|
|
120
|
+
instructions();
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
await main();
|
package/bin/enigma.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
silly boy
|
package/bin/types.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
type Options = {
|
|
4
|
+
file?: string;
|
|
5
|
+
overwrite?: boolean;
|
|
6
|
+
events?: string;
|
|
7
|
+
step?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type EnigmaConfig = {
|
|
11
|
+
enigma: {
|
|
12
|
+
name: string;
|
|
13
|
+
reflector: string;
|
|
14
|
+
rotors: string[],
|
|
15
|
+
ringSettings: number[] | string;
|
|
16
|
+
plugs: string | string[];
|
|
17
|
+
},
|
|
18
|
+
rotor?: string,
|
|
19
|
+
options?: Options,
|
|
20
|
+
encode?: string,
|
|
21
|
+
}
|
package/jsconfig.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"target": "
|
|
4
|
-
"module": "
|
|
5
|
-
|
|
3
|
+
"target": "es2022", // Or a newer version like "es2020"
|
|
4
|
+
"module": "es2022",
|
|
5
|
+
"checkJs": true,
|
|
6
|
+
"baseUrl": "./",
|
|
7
|
+
"paths": {
|
|
8
|
+
"*": ["node_modules/@types/*", "*"]
|
|
9
|
+
} },
|
|
6
10
|
"exclude": [
|
|
7
11
|
"node_modules"
|
|
8
12
|
],
|
package/lib/enigma/Encoder.js
CHANGED
|
@@ -7,11 +7,12 @@ import { STANDARD_ALPHABET } from "./consts.js";
|
|
|
7
7
|
*/
|
|
8
8
|
export default class Encoder {
|
|
9
9
|
/**
|
|
10
|
-
* Constructor for the base encoder
|
|
10
|
+
* Constructor for the base encoder. This will the parent class for all
|
|
11
|
+
* components
|
|
11
12
|
*
|
|
12
|
-
* @param {string} name
|
|
13
|
-
* @param {ComponentType} type
|
|
14
|
-
* @param {EncoderSetup} settings
|
|
13
|
+
* @param {string} name - the name of the encoder
|
|
14
|
+
* @param {ComponentType} type - the type of component
|
|
15
|
+
* @param {EncoderSetup} settings - the base settings for the encoder
|
|
15
16
|
*/
|
|
16
17
|
constructor(name, type, settings) {
|
|
17
18
|
let {cb, alphabet = STANDARD_ALPHABET} = settings;
|
|
@@ -19,6 +20,7 @@ export default class Encoder {
|
|
|
19
20
|
this.type = type;
|
|
20
21
|
this.alphabet = alphabet;
|
|
21
22
|
this.contactCount = alphabet.length;
|
|
23
|
+
|
|
22
24
|
/** @type {{[name: string]: Listener}} */
|
|
23
25
|
this.listeners = {}
|
|
24
26
|
|
|
@@ -30,8 +32,9 @@ export default class Encoder {
|
|
|
30
32
|
/**
|
|
31
33
|
* given a connector number, normalize it to be between 0 and 25 inclusive.
|
|
32
34
|
*
|
|
33
|
-
* @
|
|
35
|
+
* @protected
|
|
34
36
|
*
|
|
37
|
+
* @param {Number} connector - the connector being normalized
|
|
35
38
|
* @returns {Number} value between 0 and 25
|
|
36
39
|
*/
|
|
37
40
|
normalize(connector) {
|
|
@@ -39,10 +42,14 @@ export default class Encoder {
|
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
/**
|
|
45
|
+
* Call this method to check if the given character is a valid character in
|
|
46
|
+
* the setup alphabet. Any unexpected character except for a space will
|
|
47
|
+
* output warning to the console.
|
|
42
48
|
*
|
|
43
|
-
* @
|
|
49
|
+
* @public
|
|
44
50
|
*
|
|
45
|
-
* @
|
|
51
|
+
* @param {string} letter - the character to check
|
|
52
|
+
* @returns {boolean} true if it is false otherwise
|
|
46
53
|
*/
|
|
47
54
|
verifyLetter(letter) {
|
|
48
55
|
if (letter.length !== 1 || this.alphabet.indexOf(letter) === -1) {
|
|
@@ -56,9 +63,12 @@ export default class Encoder {
|
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
/**
|
|
59
|
-
* Call this method to convert a letter to a connector value
|
|
66
|
+
* Call this method to convert a letter to a connector value.
|
|
67
|
+
*
|
|
68
|
+
* @public
|
|
60
69
|
*
|
|
61
|
-
* @param {string} letter
|
|
70
|
+
* @param {string} letter - the character to convert. If it is not a valid
|
|
71
|
+
* letter then this function will return undefined
|
|
62
72
|
* @returns {number | undefined}
|
|
63
73
|
*/
|
|
64
74
|
letterToConnector(letter) {
|
|
@@ -72,7 +82,9 @@ export default class Encoder {
|
|
|
72
82
|
/**
|
|
73
83
|
* Call this method to turn a connector to a letter value
|
|
74
84
|
*
|
|
75
|
-
* @
|
|
85
|
+
* @public
|
|
86
|
+
*
|
|
87
|
+
* @param {number} connector - connector number to convert
|
|
76
88
|
* @returns {string | undefined}
|
|
77
89
|
*/
|
|
78
90
|
connectorToLetter(connector) {
|
|
@@ -89,11 +101,13 @@ export default class Encoder {
|
|
|
89
101
|
* numbers. The index into the array or string is the input connector, and
|
|
90
102
|
* the value at that position is the output connector
|
|
91
103
|
*
|
|
92
|
-
* @
|
|
104
|
+
* @protected
|
|
105
|
+
*
|
|
106
|
+
* @param {String} map - connections map.
|
|
93
107
|
* @returns {Array.<Number>} the numerical map
|
|
94
108
|
*/
|
|
95
109
|
makeMap(map) {
|
|
96
|
-
|
|
110
|
+
let letters = [...map];
|
|
97
111
|
return letters.map(function(letter) {
|
|
98
112
|
return this.alphabet.indexOf(letter);
|
|
99
113
|
}, this)
|
|
@@ -103,11 +117,13 @@ export default class Encoder {
|
|
|
103
117
|
* given an existing connection map from input to out put, create a new map
|
|
104
118
|
* that has the connections going in the other direction, output to input.
|
|
105
119
|
*
|
|
106
|
-
* @
|
|
120
|
+
* @protected
|
|
121
|
+
*
|
|
122
|
+
* @param {Array.<Number>} map - connection map
|
|
107
123
|
* @returns {Array.<Number>} the reversed map
|
|
108
124
|
*/
|
|
109
125
|
makeReverseMap(map) {
|
|
110
|
-
|
|
126
|
+
let reverseMap = new Array(map.length);
|
|
111
127
|
|
|
112
128
|
map.forEach(function(input, idx) {
|
|
113
129
|
reverseMap[input] = idx;
|
|
@@ -121,9 +137,11 @@ export default class Encoder {
|
|
|
121
137
|
* the given direction The default encode method just passes the input value
|
|
122
138
|
* through
|
|
123
139
|
*
|
|
124
|
-
* @
|
|
140
|
+
* @public
|
|
141
|
+
*
|
|
142
|
+
* @param {Direction} _direction - either right for moving towards the reflector
|
|
125
143
|
* or left if moving back
|
|
126
|
-
* @param {Number} input the specific connection receiving an input
|
|
144
|
+
* @param {Number} input - the specific connection receiving an input
|
|
127
145
|
*
|
|
128
146
|
* @returns {Number} The translated output connector number
|
|
129
147
|
*/
|
|
@@ -132,9 +150,13 @@ export default class Encoder {
|
|
|
132
150
|
}
|
|
133
151
|
|
|
134
152
|
/**
|
|
153
|
+
* Call this method to fire an `input` event
|
|
154
|
+
*
|
|
155
|
+
* @protected
|
|
135
156
|
*
|
|
136
|
-
* @param {number | string} input
|
|
137
|
-
*
|
|
157
|
+
* @param {number | string} input - the input value as either a letter or
|
|
158
|
+
* a number
|
|
159
|
+
* @param {Direction} direction - the direction the signal is heading
|
|
138
160
|
*/
|
|
139
161
|
fireInput(input, direction) {
|
|
140
162
|
if (typeof input === 'number') input = this.connectorToLetter(input)
|
|
@@ -153,9 +175,13 @@ export default class Encoder {
|
|
|
153
175
|
}
|
|
154
176
|
|
|
155
177
|
/**
|
|
178
|
+
* Call this method to fire an `output` even
|
|
156
179
|
*
|
|
157
|
-
* @
|
|
158
|
-
*
|
|
180
|
+
* @protected
|
|
181
|
+
*
|
|
182
|
+
* @param {number | string} output - the output value as either a letter or
|
|
183
|
+
* a number
|
|
184
|
+
* @param {Direction} direction - the direction the signal is heading
|
|
159
185
|
*/
|
|
160
186
|
fireOutput(output, direction) {
|
|
161
187
|
if (typeof output === 'number') output = this.connectorToLetter(output)
|
|
@@ -173,10 +199,15 @@ export default class Encoder {
|
|
|
173
199
|
}
|
|
174
200
|
|
|
175
201
|
/**
|
|
202
|
+
* Call this method to fire a `translate` event
|
|
203
|
+
*
|
|
204
|
+
* @protected
|
|
176
205
|
*
|
|
177
|
-
* @param {number | string} input
|
|
178
|
-
*
|
|
179
|
-
* @param {
|
|
206
|
+
* @param {number | string} input - the input value as either a letter or
|
|
207
|
+
* a number
|
|
208
|
+
* @param {number | string} output - the output value as either a letter or
|
|
209
|
+
* a number
|
|
210
|
+
* @param {Direction} direction - the direction the signal is heading
|
|
180
211
|
*/
|
|
181
212
|
fireTranslate(input, output, direction) {
|
|
182
213
|
if (typeof input === 'number') input = this.connectorToLetter(input)
|
|
@@ -195,10 +226,15 @@ export default class Encoder {
|
|
|
195
226
|
}
|
|
196
227
|
|
|
197
228
|
/**
|
|
229
|
+
* Call this method to fire the `input`, `output` and `translate` events.
|
|
230
|
+
*
|
|
231
|
+
* @protected
|
|
198
232
|
*
|
|
199
|
-
* @param {number | string} input
|
|
200
|
-
*
|
|
201
|
-
* @param {
|
|
233
|
+
* @param {number | string} input - the input value as either a letter or
|
|
234
|
+
* a number
|
|
235
|
+
* @param {number | string} output - the output value as either a letter or
|
|
236
|
+
* a number
|
|
237
|
+
* @param {Direction} direction - the direction the signal is heading
|
|
202
238
|
*/
|
|
203
239
|
fireEncodeSet(input, output, direction) {
|
|
204
240
|
this.fireInput(input, direction);
|
|
@@ -210,6 +246,8 @@ export default class Encoder {
|
|
|
210
246
|
* Call this method to add a function to be called when important events
|
|
211
247
|
* happen to a component. The name can be used to later remove the listener
|
|
212
248
|
*
|
|
249
|
+
* @public
|
|
250
|
+
*
|
|
213
251
|
* @param {string} name - the name of the listener
|
|
214
252
|
* @param {Listener} cb - the function to be called.
|
|
215
253
|
*/
|
|
@@ -218,7 +256,9 @@ export default class Encoder {
|
|
|
218
256
|
}
|
|
219
257
|
|
|
220
258
|
/**
|
|
221
|
-
* Call this method to remove a listener
|
|
259
|
+
* Call this method to remove a listener.
|
|
260
|
+
*
|
|
261
|
+
* @public
|
|
222
262
|
*
|
|
223
263
|
* @param {string} name - the name of the listener
|
|
224
264
|
*/
|
|
@@ -229,6 +269,8 @@ export default class Encoder {
|
|
|
229
269
|
/**
|
|
230
270
|
* Call this method to call any event listeners
|
|
231
271
|
*
|
|
272
|
+
* @protected
|
|
273
|
+
*
|
|
232
274
|
* @param {EventName} event - the event being fired
|
|
233
275
|
* @param {String} name - the name of the component firing the event
|
|
234
276
|
* @param {EventData} data - the event data
|
package/lib/enigma/Enigma.js
CHANGED
|
@@ -16,8 +16,8 @@ export default class Enigma extends Encoder {
|
|
|
16
16
|
* The constructor for the Enigma. This represents the unconfigurable
|
|
17
17
|
* settings of the device.
|
|
18
18
|
*
|
|
19
|
-
* @param {string} name
|
|
20
|
-
* @param {EnigmaSetup} settings
|
|
19
|
+
* @param {string} name - the name of the enigma
|
|
20
|
+
* @param {EnigmaSetup} settings - the setup options
|
|
21
21
|
*/
|
|
22
22
|
constructor(name, settings) {
|
|
23
23
|
super(name, "Enigma", settings)
|
|
@@ -43,6 +43,8 @@ export default class Enigma extends Encoder {
|
|
|
43
43
|
/**
|
|
44
44
|
* the configured rotors
|
|
45
45
|
*
|
|
46
|
+
* @public
|
|
47
|
+
*
|
|
46
48
|
* @return {Rotor[]}
|
|
47
49
|
*/
|
|
48
50
|
get rotors() {
|
|
@@ -50,6 +52,10 @@ export default class Enigma extends Encoder {
|
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
/**
|
|
55
|
+
* The configuration and setup options
|
|
56
|
+
*
|
|
57
|
+
* @public
|
|
58
|
+
*
|
|
53
59
|
* @returns {SimplifiedConfiguration & {reflector: string}}
|
|
54
60
|
*/
|
|
55
61
|
get configuration() {
|
|
@@ -59,6 +65,8 @@ export default class Enigma extends Encoder {
|
|
|
59
65
|
/**
|
|
60
66
|
* Configure the Enigma for encoding.
|
|
61
67
|
*
|
|
68
|
+
* @public
|
|
69
|
+
*
|
|
62
70
|
* @param {EnigmaConfiguration} settings - the configuration of the Enigma.
|
|
63
71
|
* These settings represent the aspects of the Enigma that can can change for daily
|
|
64
72
|
* configuration.
|
|
@@ -115,14 +123,39 @@ export default class Enigma extends Encoder {
|
|
|
115
123
|
/**
|
|
116
124
|
* Call this method to "step" the rotors one time. This method will manage the
|
|
117
125
|
* stepping between all rotors
|
|
126
|
+
*
|
|
127
|
+
* @public
|
|
118
128
|
*/
|
|
119
129
|
step() {
|
|
130
|
+
// Only the notches on the first two rotors effect stepping. One rotor's
|
|
131
|
+
// alphabet ring prevents the next rotor from turning unless the first
|
|
132
|
+
// rotor's notch is exposed. When a rotor does step, so does the
|
|
133
|
+
// previous rotor because they are attached by the notch in the
|
|
134
|
+
// previous rotor's ring
|
|
135
|
+
|
|
136
|
+
// precalculate if and why a rotor should step, the first rotor is
|
|
137
|
+
// always engaged
|
|
138
|
+
/** @type {{engaged:boolean, nextEngaged: boolean}[]} */
|
|
139
|
+
let step = []
|
|
140
|
+
|
|
141
|
+
step[0] = {engaged: true, nextEngaged: false};
|
|
142
|
+
|
|
143
|
+
for (let idx = 1; idx < 3; idx++) {
|
|
144
|
+
let engaged = this._rotors[idx - 1].atTurnover();
|
|
145
|
+
let nextEngaged = idx < 2 && this._rotors[idx].atTurnover();
|
|
146
|
+
|
|
147
|
+
step[idx] = {engaged, nextEngaged};
|
|
148
|
+
}
|
|
149
|
+
|
|
120
150
|
this._rotors.forEach((rotor, idx) => {
|
|
121
151
|
if (rotor.isFixed()) return;
|
|
122
152
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
153
|
+
if (step[idx].engaged || step[idx].nextEngaged) {
|
|
154
|
+
rotor.step();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// the double-step is caused by the the next rotor stepping
|
|
158
|
+
if (step[idx].nextEngaged) {
|
|
126
159
|
/** @type {EventData} */
|
|
127
160
|
let eventData = {
|
|
128
161
|
name: rotor.name,
|
|
@@ -131,23 +164,16 @@ export default class Enigma extends Encoder {
|
|
|
131
164
|
event: "double-step",
|
|
132
165
|
offset: rotor.offset,
|
|
133
166
|
}
|
|
134
|
-
|
|
135
167
|
this.fire('double-step', rotor.name, eventData);
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
if (this.pending[idx]) {
|
|
139
|
-
this.pending[idx] = false;
|
|
140
|
-
if (rotor.step()) this.pending[idx + 1] = true;
|
|
141
168
|
}
|
|
142
169
|
});
|
|
143
|
-
|
|
144
|
-
// The first rotor is always stepping
|
|
145
|
-
this.pending[0] = true;
|
|
146
170
|
}
|
|
147
171
|
|
|
148
172
|
/**
|
|
149
173
|
* Call this method to set the starting rotation for the messages to encrypt
|
|
150
174
|
*
|
|
175
|
+
* @public
|
|
176
|
+
*
|
|
151
177
|
* @param {number[]|string} setup - length of the string or the array
|
|
152
178
|
* should match the number of rotors and are given left to right. If start
|
|
153
179
|
* is a string then the letters of the string specify the start value seen
|
|
@@ -180,7 +206,9 @@ export default class Enigma extends Encoder {
|
|
|
180
206
|
* Call this method to simulate a keypress on the Enigma. This will output
|
|
181
207
|
* the encoded letter
|
|
182
208
|
*
|
|
183
|
-
* @
|
|
209
|
+
* @public
|
|
210
|
+
*
|
|
211
|
+
* @param {String} letter - the key pressed
|
|
184
212
|
* @returns {String | undefined} the encoded letter
|
|
185
213
|
*/
|
|
186
214
|
keyPress(letter) {
|
|
@@ -218,8 +246,10 @@ export default class Enigma extends Encoder {
|
|
|
218
246
|
/**
|
|
219
247
|
* Call this shortcut method to encode a whole string
|
|
220
248
|
*
|
|
221
|
-
* @
|
|
222
|
-
*
|
|
249
|
+
* @public
|
|
250
|
+
*
|
|
251
|
+
* @param {String} start - the starting position for the rotors
|
|
252
|
+
* @param {String} text - the text to encode
|
|
223
253
|
*
|
|
224
254
|
* @returns {String} the encoded string.
|
|
225
255
|
*/
|
|
@@ -234,9 +264,14 @@ export default class Enigma extends Encoder {
|
|
|
234
264
|
}
|
|
235
265
|
|
|
236
266
|
/**
|
|
267
|
+
* Call this method to add a function to be called when important events
|
|
268
|
+
* happen to a component. This listener will be propagated to all installed
|
|
269
|
+
* components. The name can be used to later remove the listener
|
|
270
|
+
*
|
|
271
|
+
* @public
|
|
237
272
|
*
|
|
238
|
-
* @param {string} name
|
|
239
|
-
* @param {Listener} cb
|
|
273
|
+
* @param {string} name - the name of the listener
|
|
274
|
+
* @param {Listener} cb - the function to be called.
|
|
240
275
|
*/
|
|
241
276
|
listen(name, cb) {
|
|
242
277
|
super.listen(name, cb);
|
|
@@ -247,4 +282,22 @@ export default class Enigma extends Encoder {
|
|
|
247
282
|
|
|
248
283
|
this.reflector.listen(name, cb);
|
|
249
284
|
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Call this method to unregister a listener. The listener will also be
|
|
288
|
+
* removed from all components
|
|
289
|
+
*
|
|
290
|
+
* @public
|
|
291
|
+
*
|
|
292
|
+
* @param {string} name - the name of the listener
|
|
293
|
+
*/
|
|
294
|
+
|
|
295
|
+
unlisten(name) {
|
|
296
|
+
super.unlisten(name);
|
|
297
|
+
|
|
298
|
+
for (let encoder of this.encoders) {
|
|
299
|
+
encoder.unlisten(name)
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
250
303
|
}
|
|
@@ -90,6 +90,7 @@ interface RotorSetup extends EncoderSetup {
|
|
|
90
90
|
* - **left** - the signal is moving from left to right
|
|
91
91
|
* - **turn-around** - the signal is transitioning between directions, this is
|
|
92
92
|
* sent from the reflector
|
|
93
|
+
* - **end-to-end** - this is the direction used for the Enigma object
|
|
93
94
|
*/
|
|
94
95
|
type Direction = ("right" | "left" | "turn-around" | "end-to-end");
|
|
95
96
|
|
|
@@ -146,7 +147,7 @@ type StepData = {
|
|
|
146
147
|
/** the ending rotor position */
|
|
147
148
|
stop: number;
|
|
148
149
|
|
|
149
|
-
/** the
|
|
150
|
+
/** true if the rotor has rotated to its turnover location */
|
|
150
151
|
turnover: boolean;
|
|
151
152
|
}
|
|
152
153
|
|