-
-
Notifications
You must be signed in to change notification settings - Fork 241
/
TapCode.js
150 lines (132 loc) · 3.52 KB
/
TapCode.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import ArrayUtil from '../ArrayUtil'
import PolybiusSquareEncoder from './PolybiusSquare'
import Encoder from '../Encoder'
const meta = {
name: 'tap-code',
title: 'Tap code',
category: 'Polybius square ciphers',
type: 'encoder'
}
/**
* The tap code alphabet (without the letter K)
* @type {string}
*/
const alphabet = 'abcdefghijlmnopqrstuvwxyz'
/**
* Encoder brick for tap code encoding and decoding
*/
export default class TapCodeEncoder extends Encoder {
/**
* Returns brick meta.
* @return {object}
*/
static getMeta () {
return meta
}
/**
* Constructor
*/
constructor () {
super()
this.addSettings([
{
name: 'tapMark',
label: 'Tap',
type: 'text',
width: 4,
value: '.',
minLength: 1,
randomizable: false
},
{
name: 'groupMark',
label: 'Group',
type: 'text',
width: 4,
value: ' ',
minLength: 1,
randomizable: false
},
{
name: 'letterMark',
label: 'Letter',
type: 'text',
width: 4,
value: ' ',
minLength: 1,
randomizable: false
}
])
// Create internal Polybius square encoder instance
this._polybiusSquare = new PolybiusSquareEncoder()
this._polybiusSquare.setSettingValues({
alphabet: alphabet,
rows: '12345',
columns: '12345',
separator: '',
caseSensitivity: false,
includeForeignChars: false
})
}
/**
* Performs encode on given content.
* @protected
* @param {Chain} content
* @return {number[]|string|Uint8Array|Chain} Encoded content
*/
async performEncode (content) {
const tapMark = this.getSettingValue('tapMark').getString()
const groupMark = this.getSettingValue('groupMark').getString()
const letterMark = this.getSettingValue('letterMark').getString()
// Encode content to coordinates using Polybius square
// As C and K shares the same alphabet position, replace all K chars
const coordinates = await this._polybiusSquare.encode(
content.getString().replace(/k/gi, 'c'))
const count = coordinates.getLength() / 2
let row, column
let result = ''
// Translate each set of coordinates into tap marks
for (let i = 0; i < count; i++) {
row = parseInt(coordinates.getCharAt(i * 2))
column = parseInt(coordinates.getCharAt(i * 2 + 1))
result +=
(i > 0 ? letterMark : '') +
tapMark.repeat(row) +
groupMark +
tapMark.repeat(column)
}
return result
}
/**
* Performs decode on given content.
* @protected
* @param {Chain} content
* @return {number[]|string|Uint8Array|Chain} Decoded content
*/
performDecode (content) {
const tapMark = this.getSettingValue('tapMark').getCodePoints()
const tapMarkLength = tapMark.length
const input = content.getCodePoints()
let coordinates = ''
let i = 0
let j = -1
let value = 0
// Iterate through tap marks
while ((j = ArrayUtil.indexOfSlice(input, tapMark, j + 1)) !== -1) {
// Check if the current dot is adjacent to the previous one
if (i === 0 || j === i + tapMarkLength) {
// Increase current coordinate value
value++
} else {
// Append coordinate
coordinates += value.toString()
value = 1
}
i = j
}
// Append last coordinate
coordinates += value.toString()
// Decode coordinates using Polybius square
return this._polybiusSquare.decode(coordinates)
}
}