@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.
Files changed (43) hide show
  1. package/README.md +7 -17
  2. package/bin/config.js +11 -0
  3. package/bin/enigma.js +125 -0
  4. package/bin/enigma.json +17 -0
  5. package/bin/instructions.txt +1 -0
  6. package/bin/types.d.ts +21 -0
  7. package/jsconfig.json +7 -3
  8. package/lib/enigma/Encoder.js +69 -27
  9. package/lib/enigma/Enigma.js +72 -19
  10. package/lib/enigma/EnigmaTypes.d.ts +2 -1
  11. package/lib/enigma/EntryDisc.js +9 -6
  12. package/lib/enigma/Inventory.js +11 -11
  13. package/lib/enigma/PlugBoard.js +11 -8
  14. package/lib/enigma/Reflector.js +6 -7
  15. package/lib/enigma/Rotor.js +49 -30
  16. package/lib/enigma/consts.js +1 -1
  17. package/lib/enigma/standardInventory.js +3 -3
  18. package/lib/enigma/tests/EnigmaSpec.js +14 -14
  19. package/lib/enigma/tests/RotorSpec.js +25 -25
  20. package/lib/generator/CodeBook.js +26 -7
  21. package/lib/generator/Generator.js +20 -7
  22. package/lib/utils/Random.js +12 -12
  23. package/lib/utils/options.js +63 -0
  24. package/lib/utils/optionsTypes.d.ts +5 -0
  25. package/package.json +6 -4
  26. package/scripts/test.js +5 -5
  27. package/types/enigma/Encoder.d.ts +80 -39
  28. package/types/enigma/Enigma.d.ts +28 -12
  29. package/types/enigma/EntryDisc.d.ts +2 -2
  30. package/types/enigma/Inventory.d.ts +11 -11
  31. package/types/enigma/PlugBoard.d.ts +6 -5
  32. package/types/enigma/Reflector.d.ts +2 -2
  33. package/types/enigma/Rotor.d.ts +17 -12
  34. package/types/enigma/consts.d.ts +1 -1
  35. package/types/enigma/standardInventory.d.ts +1 -1
  36. package/types/generator/CodeBook.d.ts +34 -15
  37. package/types/generator/Generator.d.ts +26 -13
  38. package/types/index.d.ts +2 -4
  39. package/types/utils/Random.d.ts +12 -12
  40. package/scripts/hamlet.html +0 -8880
  41. package/scripts/parseHamlet.js +0 -32
  42. package/scripts/test-messages.js +0 -60
  43. package/scripts/x +0 -6446
package/README.md CHANGED
@@ -20,16 +20,7 @@
20
20
 
21
21
  # Getting Started
22
22
 
23
- The Enigma toolkit is released as a nodejs ESM module. ESM is a relatively new
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
- var enigma = new Enigma("I", {reflector: 'B'});
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
- `encode(start, text)`
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
- var enigma = new Enigma("I", {reflector: 'B'});
439
+ let enigma = new Enigma("I", {reflector: 'B'});
450
440
 
451
441
  enigma.configure({
452
442
  rotors: ['III', 'VI', 'VIII'],
453
- ringSetting: [1, 8, 13],
443
+ ringSettings: [1, 8, 13],
454
444
  plugs: 'AN EZ HK IJ LR MQ OT PV SW UX'
455
445
  });
456
446
 
457
- var message = 'YKAENZAPMSCHZBFOCUVMRMDPYCOFHADZIZMEFXTHFLOLPZLFGGBOTGOXGRETDWTJIQHLMXVJWKZUASTR'
458
- var decoded = enigma.encode('UZV', message)
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
@@ -0,0 +1,11 @@
1
+
2
+ /** @type {EnigmaConfig} */
3
+ export const CONFIG = {
4
+ enigma: {
5
+ name: "I",
6
+ reflector: 'B',
7
+ rotors: ['III', 'VI', 'VIII'],
8
+ ringSettings: [1, 8, 13],
9
+ plugs: 'AN EZ HK IJ LR MQ OT PV SW UX'
10
+ }
11
+ }
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();
@@ -0,0 +1,17 @@
1
+ {
2
+ "enigma": {
3
+ "name": "I",
4
+ "reflector": "B",
5
+ "rotors": [
6
+ "III",
7
+ "VI",
8
+ "VIII"
9
+ ],
10
+ "ringSettings": [
11
+ 1,
12
+ 8,
13
+ 13
14
+ ],
15
+ "plugs": "AN EZ HK IJ LR MQ OT PV SW UX"
16
+ }
17
+ }
@@ -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": "es2020", // Or a newer version like "es2020"
4
- "module": "commonjs",
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
  ],
@@ -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
- * @param {Number} connector the connector being normalized
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
- * @param {string} letter
49
+ * @public
44
50
  *
45
- * @returns {boolean}
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
- * @param {number} connector
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
- * @param {String} map connections map.
104
+ * @protected
105
+ *
106
+ * @param {String} map - connections map.
93
107
  * @returns {Array.<Number>} the numerical map
94
108
  */
95
109
  makeMap(map) {
96
- var letters = [...map];
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
- * @param {Array.<Number>} map connection map
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
- var reverseMap = new Array(map.length);
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
- * @param {Direction} _direction either right for moving towards the reflector
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
- * @param {Direction} direction
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
- * @param {number | string} output
158
- * @param {Direction} direction
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
- * @param {number | string} output
179
- * @param {Direction} direction
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
- * @param {number | string} output
201
- * @param {Direction} direction
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
@@ -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
- // This is the double stepping. Only do this for the middle rotor
124
- if (rotor.willTurnover() && idx === 1) {
125
- this.pending[idx] = true
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
- * @param {String} letter the key pressed
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
- * @param {String} start the starting position for the rotors
222
- * @param {String} text the text to encode
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 locations of the turnover points for this rotor */
150
+ /** true if the rotor has rotated to its turnover location */
150
151
  turnover: boolean;
151
152
  }
152
153