virtual-keypad 5.9.0 → 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 +15 -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
|
|
|
@@ -67,6 +68,7 @@ class Keypad extends KeypadPeer {
|
|
|
67
68
|
break;
|
|
68
69
|
case "UpdateHeader":
|
|
69
70
|
document.getElementById("keypad-header").innerText = data.headerContent;
|
|
71
|
+
document.getElementById("keypad-header").style.display = data.headerContent === "" ? "none" : "block";
|
|
70
72
|
this.headerMessage = data.headerContent;
|
|
71
73
|
break;
|
|
72
74
|
case "UpdateFooter":
|
|
@@ -99,6 +101,10 @@ class Keypad extends KeypadPeer {
|
|
|
99
101
|
this.enableKeys();
|
|
100
102
|
}
|
|
101
103
|
break;
|
|
104
|
+
// TODO factor out into keypadPeer
|
|
105
|
+
case "Heartbeat":
|
|
106
|
+
this.lastHeartbeat = performance.now();
|
|
107
|
+
break;
|
|
102
108
|
default:
|
|
103
109
|
console.log("Message type: ", data.message);
|
|
104
110
|
}
|
|
@@ -127,7 +133,8 @@ class Keypad extends KeypadPeer {
|
|
|
127
133
|
this.conn.on("close", () => console.log("Connection closed") );
|
|
128
134
|
};
|
|
129
135
|
#initiateHandshake = () => {
|
|
130
|
-
this.conn.send({ message: "Handshake"
|
|
136
|
+
this.conn.send({ message: "Handshake" });
|
|
137
|
+
this._setupHeartBeatIntervals();
|
|
131
138
|
};
|
|
132
139
|
#prepareHTML = () => {
|
|
133
140
|
/**
|
|
@@ -155,6 +162,7 @@ class Keypad extends KeypadPeer {
|
|
|
155
162
|
const keypadFooter = document.createElement("div");
|
|
156
163
|
keypadFooter.setAttribute("id", "keypad-footer");
|
|
157
164
|
keypadElem.appendChild(keypadHeader);
|
|
165
|
+
keypadKeys.appendChild(keypadControlKeys);
|
|
158
166
|
keypadElem.appendChild(keypadKeys);
|
|
159
167
|
keypadElem.appendChild(keypadControlKeys);
|
|
160
168
|
keypadElem.appendChild(keypadFooter);
|
|
@@ -169,6 +177,8 @@ class Keypad extends KeypadPeer {
|
|
|
169
177
|
// Close connection if window closes.
|
|
170
178
|
window.onbeforeunload = () => {this.conn?.close(); console.log("closing connection on page unload.")};
|
|
171
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.")};
|
|
172
182
|
};
|
|
173
183
|
#populateKeypad = () => {
|
|
174
184
|
const buttonResponseFn = (button) => {
|
|
@@ -200,7 +210,6 @@ class Keypad extends KeypadPeer {
|
|
|
200
210
|
};
|
|
201
211
|
const createButton = (symbol) => {
|
|
202
212
|
// Create a response button for this symbol
|
|
203
|
-
// TODO why aren't these "button"s??? More accessible, make label easier. Did I have a good reason???
|
|
204
213
|
let button = document.createElement("button");
|
|
205
214
|
button.id = symbol;
|
|
206
215
|
button.className = "response-button";
|
|
@@ -235,18 +244,14 @@ class Keypad extends KeypadPeer {
|
|
|
235
244
|
button.addEventListener("touchmove", (e) => {
|
|
236
245
|
/* prevent delay and simulated mouse events */
|
|
237
246
|
e.preventDefault();
|
|
238
|
-
console.log("touchmove event: ", e);
|
|
239
247
|
});
|
|
240
248
|
button.addEventListener("touchend", (e) => {
|
|
241
249
|
/* prevent delay and simulated mouse events */
|
|
242
250
|
e.preventDefault();
|
|
243
|
-
console.log("touchend event: ", e);
|
|
244
251
|
const elementEndedOn = document.elementFromPoint(
|
|
245
252
|
e.changedTouches[0].clientX,
|
|
246
253
|
e.changedTouches[0].clientY
|
|
247
254
|
);
|
|
248
|
-
console.log("elementEndedOn: ", elementEndedOn);
|
|
249
|
-
console.log("elementEndedOn.className: ", elementEndedOn.className);
|
|
250
255
|
switch (elementEndedOn.className) {
|
|
251
256
|
case "response-button-label noselect":
|
|
252
257
|
buttonResponseFn(elementEndedOn.parentElement); // e.target.click();
|
|
@@ -293,6 +298,8 @@ class Keypad extends KeypadPeer {
|
|
|
293
298
|
this.clearKeys();
|
|
294
299
|
// Create new buttons
|
|
295
300
|
this.alphabet.forEach((symbol) => createButton(symbol));
|
|
301
|
+
// Manually style buttons, according to Denis' algorithm
|
|
302
|
+
setTimeout(() => applyMaxKeySize(this.alphabet.length), 100);
|
|
296
303
|
};
|
|
297
304
|
visualFeedbackThenReset = (delayTime = 800) => {
|
|
298
305
|
// ie grey out keys just after use, to discourage rapid response
|
|
@@ -336,8 +343,8 @@ class Keypad extends KeypadPeer {
|
|
|
336
343
|
* Remove all keys from the keypad.
|
|
337
344
|
*/
|
|
338
345
|
clearKeys = () => {
|
|
339
|
-
document.querySelector("#keypad-keys").innerHTML = "";
|
|
340
|
-
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 = "";
|
|
341
348
|
};
|
|
342
349
|
/**
|
|
343
350
|
* Return the nodes corresponding to the specified keys.
|
|
@@ -354,7 +361,6 @@ class Keypad extends KeypadPeer {
|
|
|
354
361
|
*/
|
|
355
362
|
disableKeys = (whichKeys=[]) => {
|
|
356
363
|
const keyElems = this._getKeysElements(whichKeys);
|
|
357
|
-
console.log("disabling elems", keyElems);
|
|
358
364
|
keyElems.forEach(e => {
|
|
359
365
|
e.classList.add("unpressable");
|
|
360
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
|
}
|