Skip to content

Commit f97bac2

Browse files
feat: Add Wolfenbuetteler code
1 parent b4a0066 commit f97bac2

File tree

8 files changed

+175
-2
lines changed

8 files changed

+175
-2
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ console.log(rot('Hello world!'))
5353
- Manchester code
5454
- DTMF
5555
- Base64 (with unicode support!)
56+
- Wolfenbuetteler code
5657

5758
## Contributing
5859

docs/ciphers/wolfenbuetteler.md

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Wolfenbuetteler code
2+
3+
> The Wolfenbuetteler code was invented in 1433. Thus, the cipher isn't overly complex.
4+
It is a simple substitution of vocals by the letters M, K, D, T and H.
5+
6+
## Cipher behavior information
7+
8+
* Case sensitive? ✓❌ (not by default, but only uppercase can be enabled)
9+
* Deterministic? ✓
10+
* Alphabet: **All characters, even unicode**
11+
* Characters not in alphabet will be: N/A
12+
13+
## Default options object
14+
15+
```
16+
const options = {
17+
onlyUppercase: false // Should the code transform only uppercase letters?
18+
}
19+
```
20+
21+
## Usage
22+
23+
### Encoding
24+
25+
#### Default
26+
27+
```
28+
import { wolfenbuetteler } from 'cipher-collection'
29+
30+
31+
console.log(wolfenbuetteler('EXAMPLE✓')) // KXMAPLK✓
32+
console.log(wolfenbuetteler('EXamPLE✓')) // KXmaPLK✓
33+
```
34+
35+
#### Only uppercase
36+
37+
```
38+
import { wolfenbuetteler } from 'cipher-collection'
39+
40+
const onlyUpperCaseOptions = { onlyUpperCase: true }
41+
42+
console.log(wolfenbuetteler('EXAMPLE✓', onlyUpperCaseOptions)) // KXMAPLK✓
43+
console.log(wolfenbuetteler('EXamPLE✓', onlyUpperCaseOptions)) // KXamPLK✓
44+
```
45+
46+
### Decoding
47+
48+
#### Default
49+
50+
```
51+
import { wolfenbuetteler } from 'cipher-collection'
52+
53+
54+
console.log(wolfenbuetteler('KXMAPLK✓')) // EXAMPLE✓
55+
console.log(wolfenbuetteler('KXmaPLK✓')) // EXamPLE✓
56+
```
57+
58+
#### Only uppercase
59+
60+
```
61+
import { wolfenbuetteler } from 'cipher-collection'
62+
63+
const onlyUpperCaseOptions = { onlyUpperCase: true }
64+
65+
console.log(wolfenbuetteler('KXMAPLK✓', onlyUpperCaseOptions)) // EXAMPLE✓
66+
console.log(wolfenbuetteler('KXmaPLK✓', onlyUpperCaseOptions)) // EXmaPLE✓
67+
```

docs/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@
1515
* [Manchester code](./ciphers/manchester.md)
1616
* [DTMF](./ciphers/dtmf.md)
1717
* [Base64](./ciphers/base64.md)
18+
* [Wolfenbuetteler code](./ciphers/wolfenbuetteler.md)
1819

src/helpers.js

+19
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,22 @@ export const throwOrSilent = (options, errorMessage) => {
1616
}
1717

1818
export const isBrowser = (() => typeof window === 'object')()
19+
20+
export const substitute = (input, options = {}) => {
21+
options = { ...DEFAULT_SUBSTITUTE_OPTIONS, ...options }
22+
23+
const mappingEntries = Object.entries(options.mapping)
24+
25+
return input.replace(/./g, x => {
26+
const transformedEntries = options.caseSensitive ? mappingEntries : mappingEntries.map(([k, v]) => isLowerCase(x) ? [k.toLowerCase(), v.toLowerCase()] : [k.toUpperCase(), v.toUpperCase()])
27+
const foundMapping = transformedEntries.find(([k, v]) => k === x || v === x)
28+
return !foundMapping ? x : foundMapping[0] === x ? foundMapping[1] : foundMapping[0]
29+
})
30+
}
31+
32+
const isLowerCase = x => x.toLowerCase() === x
33+
34+
const DEFAULT_SUBSTITUTE_OPTIONS = {
35+
mapping: {},
36+
caseSensitive: true
37+
}

src/index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import pollux from './pollux'
55
import multiTap from './multiTap'
66
import dtmf from './dtmf'
77
import base64 from './base64'
8+
import wolfenbuetteler from './wolfenbuetteler'
89

910
export default {
1011
rot,
@@ -13,5 +14,6 @@ export default {
1314
pollux,
1415
multiTap,
1516
dtmf,
16-
base64
17+
base64,
18+
wolfenbuetteler
1719
}

src/wolfenbuetteler.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { substitute } from './helpers'
2+
3+
export default (input, options = {}) => substitute(input, retrieveOptions(options))
4+
5+
const retrieveOptions = options => ({
6+
mapping: MAPPING,
7+
caseSensitive: options.onlyUpperCase
8+
})
9+
10+
const MAPPING = {
11+
'A': 'M',
12+
'E': 'K',
13+
'I': 'D',
14+
'O': 'T',
15+
'U': 'H'
16+
}

test/helpers.test.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { randomInRange } from 'helpers'
1+
import { randomInRange, substitute } from 'helpers'
22

33
describe('randomInRange', () => {
44
test('returns random number in range', () => {
@@ -13,3 +13,29 @@ describe('randomInRange', () => {
1313
expect(() => { randomInRange(1, 0) }).toThrowError('Min cannot be larger than max')
1414
})
1515
})
16+
17+
describe('substitute', () => {
18+
const mappingOptions = { mapping: { 'A': 'M' } }
19+
const mappingAndCaseInsensitiveOptions = { mapping: { 'A': 'M' }, caseSensitive: false }
20+
test('default', () => {
21+
expect(substitute('A')).toBe('A')
22+
})
23+
24+
test('with custom mapping', () => {
25+
expect(substitute('A', mappingOptions)).toBe('M')
26+
expect(substitute('AMAMA', mappingOptions)).toBe('MAMAM')
27+
28+
expect(substitute('B', mappingOptions)).toBe('B')
29+
})
30+
31+
test('case-insensitive', () => {
32+
expect(substitute('Aa', { caseSensitive: false })).toBe('Aa')
33+
})
34+
35+
test('case-insensitive with custom mapping', () => {
36+
expect(substitute('Aa', mappingAndCaseInsensitiveOptions)).toBe('Mm')
37+
expect(substitute('AMamA', mappingAndCaseInsensitiveOptions)).toBe('MAmaM')
38+
39+
expect(substitute('B', mappingAndCaseInsensitiveOptions)).toBe('B')
40+
})
41+
})

test/wolfenbuetteler.test.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import wolfenbuetteler from 'wolfenbuetteler'
2+
3+
const example = {
4+
cleartext: 'EXAMPLE✓',
5+
ciphertext: 'KXMAPLK✓'
6+
}
7+
const exampleMixed = {
8+
cleartext: 'ExamPLE✓',
9+
ciphertext: 'KxmaPLK✓'
10+
}
11+
12+
const onlyUppercaseOutput = {
13+
cleartext: 'ExmaPLE✓',
14+
ciphertext: 'KxamPLK✓'
15+
}
16+
17+
const onlyUppercase = { onlyUpperCase: true }
18+
19+
describe('encoding', () => {
20+
test('default', () => {
21+
expect(wolfenbuetteler(example.cleartext)).toBe(example.ciphertext)
22+
expect(wolfenbuetteler(exampleMixed.cleartext)).toBe(exampleMixed.ciphertext)
23+
})
24+
25+
test('only uppercase', () => {
26+
expect(wolfenbuetteler(example.cleartext, onlyUppercase)).toBe(example.ciphertext)
27+
expect(wolfenbuetteler(exampleMixed.cleartext, onlyUppercase)).toBe(onlyUppercaseOutput.ciphertext)
28+
})
29+
})
30+
31+
describe('decoding', () => {
32+
test('default', () => {
33+
expect(wolfenbuetteler(example.ciphertext)).toBe(example.cleartext)
34+
expect(wolfenbuetteler(exampleMixed.ciphertext)).toBe(exampleMixed.cleartext)
35+
})
36+
37+
test('only uppercase', () => {
38+
expect(wolfenbuetteler(example.ciphertext, onlyUppercase)).toBe(example.cleartext)
39+
expect(wolfenbuetteler(exampleMixed.ciphertext, onlyUppercase)).toBe(onlyUppercaseOutput.cleartext)
40+
})
41+
})

0 commit comments

Comments
 (0)