virtual-keypad 5.9.1 → 5.10.0
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/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
- package/src/keypad.css +35 -31
- package/src/keypad.js +14 -9
- package/src/keypadPeer.js +31 -7
- package/src/maxKeySize.js +82 -0
- package/src/receiver.js +5 -1
package/package.json
CHANGED
package/src/keypad.css
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* REMOTE KEYPAD STYLING */
|
|
2
2
|
#keypad {
|
|
3
|
+
position: absolute;
|
|
3
4
|
width: 100%;
|
|
4
5
|
height: 100vh;
|
|
5
6
|
height: 100dvh;
|
|
@@ -7,12 +8,11 @@
|
|
|
7
8
|
display: grid;
|
|
8
9
|
grid-template-columns: 1fr;
|
|
9
10
|
/* Make header text as big as the text, keys take up as much space as needed, and control keys take up bottom space */
|
|
10
|
-
grid-template-rows: max-content auto
|
|
11
|
+
grid-template-rows: max-content auto;
|
|
11
12
|
|
|
12
13
|
align-items: center;
|
|
13
14
|
justify-content: center;
|
|
14
15
|
|
|
15
|
-
padding: 1em;
|
|
16
16
|
font-family: sans-serif;
|
|
17
17
|
}
|
|
18
18
|
#keypad-header {
|
|
@@ -50,34 +50,38 @@
|
|
|
50
50
|
|
|
51
51
|
padding: 10px;
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
overflow-x: visible;
|
|
53
|
+
margin: 15px;
|
|
55
54
|
|
|
56
|
-
|
|
55
|
+
/* overflow-y: auto;
|
|
56
|
+
overflow-x: visible; */
|
|
57
|
+
|
|
58
|
+
/* width: 100%;
|
|
57
59
|
height: 100%;
|
|
58
60
|
display: flex;
|
|
59
61
|
flex-wrap: wrap-reverse;
|
|
60
62
|
flex-direction: row;
|
|
61
63
|
column-gap: 5px;
|
|
62
|
-
row-gap: 5px;
|
|
64
|
+
row-gap: 5px; */
|
|
63
65
|
|
|
64
66
|
/* Horizontal shift of buttons */
|
|
65
|
-
justify-content: center;
|
|
67
|
+
/* justify-content: center; */
|
|
66
68
|
|
|
67
69
|
/* Vertical shift of buttons */
|
|
68
|
-
align-items: center;
|
|
70
|
+
/* align-items: center; */
|
|
69
71
|
|
|
70
72
|
/* Hide scrollbar */
|
|
71
|
-
-ms-overflow-style: none;
|
|
72
|
-
scrollbar-width: none;
|
|
73
|
+
/* -ms-overflow-style: none;
|
|
74
|
+
scrollbar-width: none; */
|
|
73
75
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
/* Hide scrollbar */
|
|
77
|
+
/* #keypad-keys::-webkit-scrollbar {
|
|
76
78
|
display: none;
|
|
77
|
-
}
|
|
79
|
+
} */
|
|
78
80
|
|
|
79
81
|
.response-button {
|
|
80
|
-
|
|
82
|
+
cursor: pointer;
|
|
83
|
+
|
|
84
|
+
/* width: 100%;
|
|
81
85
|
height: 100%;
|
|
82
86
|
max-width: min(25vw, 25vh, 150px);
|
|
83
87
|
max-height: min(25vw, 25vh, 150px);
|
|
@@ -101,46 +105,46 @@
|
|
|
101
105
|
font-weight: 700;
|
|
102
106
|
|
|
103
107
|
|
|
104
|
-
box-shadow:
|
|
105
|
-
-
|
|
108
|
+
box-shadow: 1px 1px 2px #888888,
|
|
109
|
+
-1px -1px 2px #fff;
|
|
106
110
|
}
|
|
107
111
|
.response-button:active {
|
|
108
|
-
box-shadow: 1px
|
|
112
|
+
box-shadow: 1px 2px 2px 1px #999;
|
|
109
113
|
background-color: #aaa;
|
|
110
114
|
}
|
|
111
115
|
|
|
112
|
-
#keypad-control-keys {
|
|
116
|
+
/* #keypad-control-keys {
|
|
113
117
|
grid-row: 3;
|
|
114
118
|
|
|
115
119
|
display: grid;
|
|
116
120
|
grid-template-columns: repeat(2, 1fr);
|
|
117
121
|
align-items: center;
|
|
118
122
|
justify-content: center;
|
|
119
|
-
}
|
|
120
|
-
#keypad-control-keys > button {
|
|
123
|
+
} */
|
|
124
|
+
/* #keypad-control-keys > button {
|
|
121
125
|
height: 95%;
|
|
122
126
|
width: 95%;
|
|
123
127
|
min-width: 95%;
|
|
124
128
|
max-height: min(15vh, 150px);
|
|
125
|
-
}
|
|
129
|
+
} */
|
|
126
130
|
|
|
127
131
|
.response-button#SPACE {
|
|
128
132
|
/* TODO actually make sure test doesn't clip */
|
|
129
|
-
font-size:
|
|
130
|
-
grid-column: 1;
|
|
131
|
-
grid-row: 1;
|
|
133
|
+
font-size: larger;
|
|
134
|
+
/* grid-column: 1;
|
|
135
|
+
grid-row: 1; */
|
|
132
136
|
}
|
|
133
137
|
.response-button#RETURN {
|
|
134
138
|
/* TODO actually make sure test doesn't clip */
|
|
135
|
-
font-size:
|
|
136
|
-
grid-column: 2;
|
|
137
|
-
grid-row: 1;
|
|
139
|
+
font-size: larger;
|
|
140
|
+
/* grid-column: 2;
|
|
141
|
+
grid-row: 1; */
|
|
138
142
|
}
|
|
139
143
|
/* Only do on a hover-enabled device, ie not a phone/tablet */
|
|
140
144
|
@media (hover: hover) {
|
|
141
145
|
.response-button:hover {
|
|
142
|
-
box-shadow: inset
|
|
143
|
-
inset -
|
|
146
|
+
box-shadow: inset 1px 1px 2px #888888,
|
|
147
|
+
inset -1px -1px 2px #fff;
|
|
144
148
|
}
|
|
145
149
|
}
|
|
146
150
|
|
|
@@ -167,8 +171,8 @@
|
|
|
167
171
|
|
|
168
172
|
.unpressable {
|
|
169
173
|
background-color: #aaaaaa;
|
|
170
|
-
box-shadow: inset
|
|
171
|
-
inset -
|
|
174
|
+
box-shadow: inset 1px 1px 2px #888888,
|
|
175
|
+
inset -1px -1px 2px #fff;
|
|
172
176
|
pointer-events: none;
|
|
173
177
|
user-select: none;
|
|
174
178
|
}
|
package/src/keypad.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applyMaxKeySize } from "./maxKeySize";
|
|
1
2
|
import "./keypad.css";
|
|
2
3
|
import { KeypadPeer, keypadUrl } from "./keypadPeer.js";
|
|
3
4
|
|
|
@@ -100,6 +101,10 @@ class Keypad extends KeypadPeer {
|
|
|
100
101
|
this.enableKeys();
|
|
101
102
|
}
|
|
102
103
|
break;
|
|
104
|
+
// TODO factor out into keypadPeer
|
|
105
|
+
case "Heartbeat":
|
|
106
|
+
this.lastHeartbeat = performance.now();
|
|
107
|
+
break;
|
|
103
108
|
default:
|
|
104
109
|
console.log("Message type: ", data.message);
|
|
105
110
|
}
|
|
@@ -128,7 +133,8 @@ class Keypad extends KeypadPeer {
|
|
|
128
133
|
this.conn.on("close", () => console.log("Connection closed") );
|
|
129
134
|
};
|
|
130
135
|
#initiateHandshake = () => {
|
|
131
|
-
this.conn.send({ message: "Handshake"
|
|
136
|
+
this.conn.send({ message: "Handshake" });
|
|
137
|
+
this._setupHeartBeatIntervals();
|
|
132
138
|
};
|
|
133
139
|
#prepareHTML = () => {
|
|
134
140
|
/**
|
|
@@ -156,6 +162,7 @@ class Keypad extends KeypadPeer {
|
|
|
156
162
|
const keypadFooter = document.createElement("div");
|
|
157
163
|
keypadFooter.setAttribute("id", "keypad-footer");
|
|
158
164
|
keypadElem.appendChild(keypadHeader);
|
|
165
|
+
keypadKeys.appendChild(keypadControlKeys);
|
|
159
166
|
keypadElem.appendChild(keypadKeys);
|
|
160
167
|
keypadElem.appendChild(keypadControlKeys);
|
|
161
168
|
keypadElem.appendChild(keypadFooter);
|
|
@@ -170,6 +177,8 @@ class Keypad extends KeypadPeer {
|
|
|
170
177
|
// Close connection if window closes.
|
|
171
178
|
window.onbeforeunload = () => {this.conn?.close(); console.log("closing connection on page unload.")};
|
|
172
179
|
window.onvisibilitychange = () => {this.conn?.close(); console.log("closing connection on page unload.")};
|
|
180
|
+
|
|
181
|
+
window.onresize = () => {const resize = () => applyMaxKeySize(this.alphabet?.length); setTimeout(resize, 10); console.log("Repositioning buttons due to resize.")};
|
|
173
182
|
};
|
|
174
183
|
#populateKeypad = () => {
|
|
175
184
|
const buttonResponseFn = (button) => {
|
|
@@ -201,7 +210,6 @@ class Keypad extends KeypadPeer {
|
|
|
201
210
|
};
|
|
202
211
|
const createButton = (symbol) => {
|
|
203
212
|
// Create a response button for this symbol
|
|
204
|
-
// TODO why aren't these "button"s??? More accessible, make label easier. Did I have a good reason???
|
|
205
213
|
let button = document.createElement("button");
|
|
206
214
|
button.id = symbol;
|
|
207
215
|
button.className = "response-button";
|
|
@@ -236,18 +244,14 @@ class Keypad extends KeypadPeer {
|
|
|
236
244
|
button.addEventListener("touchmove", (e) => {
|
|
237
245
|
/* prevent delay and simulated mouse events */
|
|
238
246
|
e.preventDefault();
|
|
239
|
-
console.log("touchmove event: ", e);
|
|
240
247
|
});
|
|
241
248
|
button.addEventListener("touchend", (e) => {
|
|
242
249
|
/* prevent delay and simulated mouse events */
|
|
243
250
|
e.preventDefault();
|
|
244
|
-
console.log("touchend event: ", e);
|
|
245
251
|
const elementEndedOn = document.elementFromPoint(
|
|
246
252
|
e.changedTouches[0].clientX,
|
|
247
253
|
e.changedTouches[0].clientY
|
|
248
254
|
);
|
|
249
|
-
console.log("elementEndedOn: ", elementEndedOn);
|
|
250
|
-
console.log("elementEndedOn.className: ", elementEndedOn.className);
|
|
251
255
|
switch (elementEndedOn.className) {
|
|
252
256
|
case "response-button-label noselect":
|
|
253
257
|
buttonResponseFn(elementEndedOn.parentElement); // e.target.click();
|
|
@@ -294,6 +298,8 @@ class Keypad extends KeypadPeer {
|
|
|
294
298
|
this.clearKeys();
|
|
295
299
|
// Create new buttons
|
|
296
300
|
this.alphabet.forEach((symbol) => createButton(symbol));
|
|
301
|
+
// Manually style buttons, according to Denis' algorithm
|
|
302
|
+
setTimeout(() => applyMaxKeySize(this.alphabet.length), 100);
|
|
297
303
|
};
|
|
298
304
|
visualFeedbackThenReset = (delayTime = 800) => {
|
|
299
305
|
// ie grey out keys just after use, to discourage rapid response
|
|
@@ -337,8 +343,8 @@ class Keypad extends KeypadPeer {
|
|
|
337
343
|
* Remove all keys from the keypad.
|
|
338
344
|
*/
|
|
339
345
|
clearKeys = () => {
|
|
340
|
-
document.querySelector("#keypad-keys").innerHTML = "";
|
|
341
|
-
document.querySelector("#keypad-control-keys").innerHTML = "";
|
|
346
|
+
document.querySelector("#keypad-keys").innerHTML = "<div id='keypad-control-keys'></div>";
|
|
347
|
+
// document.querySelector("#keypad-control-keys").innerHTML = "";
|
|
342
348
|
};
|
|
343
349
|
/**
|
|
344
350
|
* Return the nodes corresponding to the specified keys.
|
|
@@ -355,7 +361,6 @@ class Keypad extends KeypadPeer {
|
|
|
355
361
|
*/
|
|
356
362
|
disableKeys = (whichKeys=[]) => {
|
|
357
363
|
const keyElems = this._getKeysElements(whichKeys);
|
|
358
|
-
console.log("disabling elems", keyElems);
|
|
359
364
|
keyElems.forEach(e => {
|
|
360
365
|
e.classList.add("unpressable");
|
|
361
366
|
e.classList.add("noselect");
|
package/src/keypadPeer.js
CHANGED
|
@@ -17,8 +17,6 @@ export class KeypadPeer {
|
|
|
17
17
|
targetElementId: null,
|
|
18
18
|
}
|
|
19
19
|
) {
|
|
20
|
-
/* Create the Peer object for our end of the connection. */
|
|
21
|
-
this.peer = new Peer(null, { debug: 2 });
|
|
22
20
|
this.conn = null;
|
|
23
21
|
this.lastPeerId = null;
|
|
24
22
|
this.keypadUrl = parameters.hasOwnProperty("keypadUrl")
|
|
@@ -27,6 +25,16 @@ export class KeypadPeer {
|
|
|
27
25
|
this.targetElement = parameters.hasOwnProperty("targetElementId")
|
|
28
26
|
? parameters.targetElementId
|
|
29
27
|
: null;
|
|
28
|
+
// TODO add support for ttd (ms)
|
|
29
|
+
this.ttd = parameters.hasOwnProperty("ttd") ? parameters.hasOwnProperty("ttd") : 8000;
|
|
30
|
+
// TODO add support for heartRate (ms)
|
|
31
|
+
this.heartbeatIntervalMs = parameters.hasOwnProperty("heartRate") ? parameters.hasOwnProperty("heartRate") : 2000;
|
|
32
|
+
this.lastHeartbeat = performance.now();
|
|
33
|
+
this.heartBeatInterval = undefined;
|
|
34
|
+
this.heartCheckInterval = undefined
|
|
35
|
+
|
|
36
|
+
/* Create the Peer object for our end of the connection. */
|
|
37
|
+
this.peer = new Peer(null, { pingInterval: this.heartbeatIntervalMs, debug: 2 });
|
|
30
38
|
|
|
31
39
|
this.alphabet = null;
|
|
32
40
|
this.font = null;
|
|
@@ -86,7 +94,7 @@ export class KeypadPeer {
|
|
|
86
94
|
// STRING : ok
|
|
87
95
|
if (
|
|
88
96
|
proposedAlphabet.toUpperCase() === "SPACE" ||
|
|
89
|
-
proposedAlphabet.toUpperCase() == "
|
|
97
|
+
proposedAlphabet.toUpperCase() == "RETURN"
|
|
90
98
|
) {
|
|
91
99
|
validAlphabet = [proposedAlphabet];
|
|
92
100
|
} else {
|
|
@@ -95,25 +103,41 @@ export class KeypadPeer {
|
|
|
95
103
|
} else {
|
|
96
104
|
// SOMETHING ELSE : bad
|
|
97
105
|
console.error(
|
|
98
|
-
"Error! Alphabet must be specified as an array of symbols, including '
|
|
106
|
+
"Error! Alphabet must be specified as an array of symbols, including 'RETURN', 'SPACE'"
|
|
99
107
|
);
|
|
100
108
|
validAlphabet = [];
|
|
101
109
|
}
|
|
102
110
|
// Return unique elements, see: https://stackoverflow.com/questions/11246758/how-to-get-unique-values-in-an-array
|
|
103
111
|
const uniqueValidAlphabet = [...new Set(validAlphabet)];
|
|
104
112
|
|
|
105
|
-
// Order alphabet so that if 'SPACE' and '
|
|
113
|
+
// Order alphabet so that if 'SPACE' and 'RETURN' are in the list, they are correctly positioned
|
|
106
114
|
if ("SPACE" in uniqueValidAlphabet) {
|
|
107
115
|
uniqueValidAlphabet = moveElementToEndOfArray(
|
|
108
116
|
uniqueValidAlphabet,
|
|
109
117
|
"SPACE"
|
|
110
118
|
);
|
|
111
119
|
}
|
|
112
|
-
if ("
|
|
113
|
-
uniqueValidAlphabet = moveElementToEndOfArray(uniqueValidAlphabet, "
|
|
120
|
+
if ("RETURN" in uniqueValidAlphabet) {
|
|
121
|
+
uniqueValidAlphabet = moveElementToEndOfArray(uniqueValidAlphabet, "RETURN");
|
|
114
122
|
}
|
|
115
123
|
return uniqueValidAlphabet;
|
|
116
124
|
};
|
|
125
|
+
_setupHeartBeatIntervals = () => {
|
|
126
|
+
this.heartBeatInterval = setInterval(() => this.conn?.send({ message: "Heartbeat", }) , this.heartbeatIntervalMs);
|
|
127
|
+
|
|
128
|
+
this.heartCheckInterval = setInterval(() => {
|
|
129
|
+
const timeSinceHeartbeatMs = performance.now() - this.lastHeartbeat;
|
|
130
|
+
if (timeSinceHeartbeatMs > this.ttd) {
|
|
131
|
+
console.log("Closing connection due to lack of heartbeat.");
|
|
132
|
+
this.conn?.close();
|
|
133
|
+
this.conn = undefined;
|
|
134
|
+
clearInterval(this.heartBeatInterval);
|
|
135
|
+
clearInterval(this.heartCheckInterval);
|
|
136
|
+
this.heartBeatInterval = undefined;
|
|
137
|
+
this.heartCheckInterval = undefined;
|
|
138
|
+
}
|
|
139
|
+
}, this.ttd);
|
|
140
|
+
};
|
|
117
141
|
}
|
|
118
142
|
|
|
119
143
|
const moveElementToEndOfArray = (array, element) => {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
|
|
2
|
+
const getKeysDimensions = (elem, n, aspect=0.5) => {
|
|
3
|
+
/**** translation of maxKeySize.m, original provided courtesy of Prof Denis Pelli ****/
|
|
4
|
+
let keyHeightPx;
|
|
5
|
+
|
|
6
|
+
// key = aspect*keyHeightPx × keyHeightPx
|
|
7
|
+
const widthPx = elem.clientWidth;
|
|
8
|
+
const heightPx = elem.clientHeight;
|
|
9
|
+
let screenArea=widthPx*heightPx
|
|
10
|
+
|
|
11
|
+
keyHeightPx= (-1+Math.sqrt(1+4*n*heightPx/widthPx))/(2*n/widthPx);
|
|
12
|
+
keyHeightPx = (-1+Math.sqrt(1+4*n*heightPx*aspect/widthPx))/(2*aspect*n/widthPx);
|
|
13
|
+
|
|
14
|
+
console.log(`n=${n}, aspect=${aspect}, widthPx=${widthPx}, heightPx=${heightPx}`);
|
|
15
|
+
console.log(`100% efficiency would require keyHightPx ${keyHeightPx.toFixed(1)}`);
|
|
16
|
+
while (n > (Math.floor(heightPx/keyHeightPx - 1)*Math.floor(widthPx/(aspect*keyHeightPx)))){
|
|
17
|
+
// Round size down so that screen is a multiple.
|
|
18
|
+
const ss=[heightPx/Math.ceil(heightPx/keyHeightPx), widthPx/Math.ceil(widthPx/(aspect*keyHeightPx))];
|
|
19
|
+
for (let i=0; i<=ss.length; i++) {
|
|
20
|
+
// We’ve already rejected the current size, so only consider smaller sizes.
|
|
21
|
+
// This guarantees that sizePx will decrease on every loop iteration.
|
|
22
|
+
if (ss[i]>=keyHeightPx) ss[i]=0;
|
|
23
|
+
}
|
|
24
|
+
// We want the largest possible size so try the largest of the current options.
|
|
25
|
+
keyHeightPx=Math.max(...ss);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Compute efficiency as area covered by keys divided by screen area.
|
|
29
|
+
// First term for n keys. Second term is for Space and Return, which fully
|
|
30
|
+
// occupy their row.
|
|
31
|
+
const areaOfKeys=aspect*keyHeightPx*keyHeightPx*n + widthPx*keyHeightPx;
|
|
32
|
+
screenArea=heightPx*widthPx;
|
|
33
|
+
const numKeysHorizontally = Math.floor(widthPx/(aspect*keyHeightPx));
|
|
34
|
+
const numKeysVertically = Math.floor(heightPx/keyHeightPx-1);
|
|
35
|
+
const efficiency = 100*areaOfKeys/screenArea;
|
|
36
|
+
console.log(`Best keyheightPx ${keyHeightPx} px. ${numKeysHorizontally} horizontally x ${numKeysVertically} vertically. Efficiency ${efficiency}`)
|
|
37
|
+
|
|
38
|
+
return {keyHeightPx: keyHeightPx, cols: numKeysHorizontally, rows:numKeysVertically, widthPx: widthPx, heightPx: heightPx}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const applyMaxKeySize = (numberOfKeys) => {
|
|
42
|
+
const aspect = 1;
|
|
43
|
+
const keysElem = document.getElementById("keypad");
|
|
44
|
+
const {keyHeightPx, cols, rows, widthPx, heightPx} = getKeysDimensions(keysElem, numberOfKeys, aspect);
|
|
45
|
+
const keyElems = [...keysElem.getElementsByClassName("response-button")];
|
|
46
|
+
const controlKeyElemsMask = keyElems.map(e => e.parentNode.id === "keypad-control-keys");
|
|
47
|
+
const gridCoords = keyElems.filter((k,i) => !controlKeyElemsMask[i]).map((k,i) => [Math.floor(i/cols), (cols-1)-(i%cols)]);
|
|
48
|
+
const widthUsed = cols*(keyHeightPx*aspect);
|
|
49
|
+
const heightUsed = rows*keyHeightPx + keyHeightPx;
|
|
50
|
+
console.log("gridCords", gridCoords);
|
|
51
|
+
const freeHeight = heightPx - heightUsed;
|
|
52
|
+
const freeWidth = widthPx - widthUsed;
|
|
53
|
+
const verticalMarginOffset = Math.floor(freeHeight/2);
|
|
54
|
+
const horizontalMarginOffset = Math.floor(freeWidth/2);
|
|
55
|
+
|
|
56
|
+
let j=0;
|
|
57
|
+
keyElems.forEach((k,i) => {
|
|
58
|
+
k.style.position = "fixed";
|
|
59
|
+
const controlKey = controlKeyElemsMask[i];
|
|
60
|
+
const height = `${keyHeightPx}px`;
|
|
61
|
+
let top, left, width;
|
|
62
|
+
if (controlKey) {
|
|
63
|
+
top = (heightPx-keyHeightPx) - verticalMarginOffset + "px";
|
|
64
|
+
left = (k.innerText.toLowerCase() === "space" ? 0 : widthPx/2 - horizontalMarginOffset) + horizontalMarginOffset + "px";
|
|
65
|
+
width = `${(widthPx - horizontalMarginOffset*2)/2}px`;
|
|
66
|
+
} else {
|
|
67
|
+
width = keyHeightPx*aspect;
|
|
68
|
+
const [y,x] = gridCoords[j];
|
|
69
|
+
j += 1;
|
|
70
|
+
console.log(`[x,y]: [${x},${y}]`);
|
|
71
|
+
top = y*keyHeightPx + verticalMarginOffset + "px";
|
|
72
|
+
left = `${x*width+horizontalMarginOffset}px`;
|
|
73
|
+
width += "px";
|
|
74
|
+
}
|
|
75
|
+
console.log(`${k.id}, h: ${height}, w: ${width}, top: ${top}, left: ${left}`);
|
|
76
|
+
k.style.width = width;
|
|
77
|
+
k.style.height = height;
|
|
78
|
+
k.style.top = top;
|
|
79
|
+
k.style.left = left;
|
|
80
|
+
});
|
|
81
|
+
console.log("\n");
|
|
82
|
+
};
|
package/src/receiver.js
CHANGED
|
@@ -22,7 +22,7 @@ class Receiver extends KeypadPeer {
|
|
|
22
22
|
this.font = keypadParameters["font"]; // What fontface to display the symbols in
|
|
23
23
|
|
|
24
24
|
this.onData = onDataCallback; // What to do on a button-press
|
|
25
|
-
this.onHandshake = handshakeCallback; // What to do when the connection is established
|
|
25
|
+
this.onHandshake = () => {handshakeCallback(); this._setupHeartBeatIntervals();} // What to do when the connection is established
|
|
26
26
|
this.onConnection = (connection) => {this.#onPeerConnection(connection); customConnectionCallback(connection)};
|
|
27
27
|
this.onClose = () => {this.onPeerClose(); customCloseCallback()};
|
|
28
28
|
this.onError = (err) => {this.onPeerError(err); customErrorCallback(err)};
|
|
@@ -198,6 +198,10 @@ class Receiver extends KeypadPeer {
|
|
|
198
198
|
case "Keypress":
|
|
199
199
|
this.onData(data);
|
|
200
200
|
break;
|
|
201
|
+
// TODO factor out into keypadPeer
|
|
202
|
+
case "Heartbeat":
|
|
203
|
+
this.lastHeartbeat = performance.now();
|
|
204
|
+
break;
|
|
201
205
|
default:
|
|
202
206
|
console.log("Message type: ", data.message);
|
|
203
207
|
}
|