-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.html
372 lines (322 loc) · 10.6 KB
/
app.html
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/css/xterm.css" />
<style>
#term-box {
overflow: hidden;
width: auto;
height: auto;
/* <- fixed height */
font-size: 18px;
line-height: 20px;
/* <- initial line-height */
}
.inputMode {
/* border: 5px outset red; */
background-color: lightblue;
text-align: left;
color: black;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-weight: bold;
}
input[type=button] {
background-color: black;
background-image: radial-gradient(rgba(0, 150, 0, 0.75), black 120%);
border: white;
color: white;
padding: 1px 1px;
/* text-decoration: none; */
/* margin: 4px 2px; */
cursor: pointer;
font: 1.3rem Courier New;
text-shadow: 0 0 5px #C8C8C8;
}
input[type=text] {
background-color:#C8C8C8;
border: white;
border-width: thick;
/* color: white; */
padding: 1px 1px;
text-decoration: none;
font: 1.3rem Courier New;
text-shadow: 0 0 5px #C8C8C8;
}
/* https://css-tricks.com/old-timey-terminal-styling/ */
body {
background-color: black;
background-image: radial-gradient(rgba(0, 150, 0, 0.75), black 120%);
height: 100vh;
margin: 0;
overflow: hidden;
padding: 2rem;
color: white;
font: 1.3rem Courier New;
text-shadow: 0 0 5px #C8C8C8;
}
::selection {
background: #0080FF;
text-shadow: none;
}
pre {
margin: 0;
}
</style>
<script>
"use strict";
var mqtt_client_id = "";
var sub_topic = "";
var pub_topic_control = "";
var pub_topic = "";
var pub_topic_paste = "";
var channelId = "";
var display_name = "";
var mqtt_broker_connected = false;
var client = null;
var character_mode = false;
var current_line = "";
var channel_set = false;
var term = null;
function initTerminal() {
document.getElementById('displayName').value = localStorage['display_name'] || "";
term = new Terminal({
cursorBlink: "block",
fontFamily: 'Courier New',
fontSize: 18,
rows: 30,
cols: 140,
});
term.open(document.getElementById("terminal"));
term.write(`WELCOME TO ALTAIR TERMINAL\r\n\r\n`);
// term.write(`WELCOME TO ALTAIR TERMINAL ${clientPrincipal.userDetails}\r\n\r\n`);
term.prompt = (msg) => {
if (mqtt_broker_connected) {
const message = new Paho.MQTT.Message(msg);
message.destinationName = pub_topic;
client.send(message);
}
};
// paste value
term.on("paste", function (data) {
if (character_mode) { return; }
if (data.length < 256) {
current_line += data;
term.write(data);
return;
}
data += "\r"
var chunks = chunkSubstr(data, 256);
chunks.forEach(element => {
if (mqtt_broker_connected) {
const message = new Paho.MQTT.Message(element);
message.destinationName = pub_topic_paste;
client.send(message);
term.write(element);
}
});
current_line = "";
});
term.prompt("");
term.on("key", function (key, ev) {
if (ev.ctrlKey) { // ctrl keys
if (ev.keyCode === 76) { // hook ctrl L as toggel between line mode and wordmaster mode
character_mode = !character_mode;
if (character_mode) {
document.getElementById("inputmode").innerHTML = "Character input mode : Wordmaster (Ctrl+L to toggle)";
} else {
document.getElementById("inputmode").innerHTML = "Line input mode: Default (Ctrl+L to toggle)";
}
return;
}
sendControl(String.fromCharCode(ev.keyCode));
return;
}
if (ev.keyCode === 27) { // escape
term.prompt(key);
term.write(key);
return;
}
if (character_mode) {
switch (ev.keyCode) {
case 39: // cursor right
sendControl(String.fromCharCode(68)); // ctrl d
return;
case 37: // cursor left
sendControl(String.fromCharCode(83)); // ctrl s
return;
case 38: // cursor up
sendControl(String.fromCharCode(69)); // ctrl e
return;
case 40: // cursor down
sendControl(String.fromCharCode(88)); // ctrl x
return;
case 45: // insert toggle
sendControl(String.fromCharCode(79)); // ctrl o
return;
case 46: // delete
sendControl(String.fromCharCode(71)); // ctrl g
return;
case 13: // Enter
term.prompt("\r");
current_line = "";
return;
case 45: // Insert
sendControl(String.fromCharCode(79));
return;
case 8: // Backspace
sendControl(String.fromCharCode(72));
return;
default:
current_line += key;
term.prompt(key);
term.write(key);
return;
}
}
if (!character_mode) {
switch (ev.keyCode) {
case 39: // cursor right
break;
case 37: // cursor left
break;
case 38: // cursor up
break;
case 40: // cursor down
break;
case 13: // Enter
current_line += "\r";
term.prompt(current_line);
term.write("\r");
current_line = "";
return;
case 8: // Backspace
if (current_line) {
current_line = current_line.slice(0, current_line.length - 1);
term.write("\b \b");
}
return;
default:
current_line += key;
term.write(key);
}
}
});
}
function chunkSubstr(str, size) {
const numChunks = Math.ceil(str.length / size)
const chunks = new Array(numChunks)
for (let i = 0, o = 0; i < numChunks; ++i, o += size) {
chunks[i] = str.substr(o, size)
}
return chunks
}
const getChannelId = async (displayName) => {
const url = `api/getchannelid?displayname=${displayName}`;
document.getElementById("connectState").innerHTML = "MQTT Broker: Connecting...";
const response = await fetch(url);
if (response.ok) { // if HTTP-status is 200-299
const json = await response.json();
if (json['DesiredChannelId']) {
channelId = json.DesiredChannelId;
channel_set = true;
generateTopics();
mqttInit();
}
} else {
alert("HTTP-Error: " + response.status + ". Check the IoT Central device name you entered.");
document.getElementById("connectState").innerHTML = "MQTT Broker: Not connected";
}
}
function connect() {
display_name = document.getElementById("displayName").value;
if (display_name == '') {
alert('ERROR: Missing the IoT Central device name')
} else {
localStorage['display_name'] = display_name;
if (mqtt_broker_connected) {
client.disconnect();
mqtt_broker_connected = false;
document.getElementById("connectState").innerHTML = "MQTT Broker: Not connected";
}
getChannelId(display_name);
}
}
function generateTopics() {
mqtt_client_id = `${channelId}`
sub_topic = `altair/${channelId}/web`;
pub_topic = `altair/${channelId}/dev`;
pub_topic_control = `altair/${channelId}/dev/ctrl`;
pub_topic_paste = `altair/${channelId}/dev/paste`
}
async function mqttInit() {
const res = await fetch("/api/mqtt-settings");
const mqttCredentials = await res.json();
// https://stackoverflow.com/questions/53051679/how-can-i-use-tls-with-paho-mqtt-over-javascript
client = new Paho.MQTT.Client(
mqttCredentials.broker,
parseInt(mqttCredentials.brokerPort, 10) || 8083,
"",
"altair" + new Date().getTime()
);
// set callback handlers
client.onConnectionLost = onConnectionLost;
client.onMessageArrived = onMessageArrived;
// connect the client
// https://www.eclipse.org/paho/files/jsdoc/index.html
client.connect({
keepAliveInterval: 30,
mqttVersion: 4,
cleanSession: true,
useSSL: true,
onSuccess: onConnect,
userName: mqttCredentials.userName,
password: mqttCredentials.password,
});
}
function sendControl(msg) {
if (mqtt_broker_connected) {
const message = new Paho.MQTT.Message(msg);
message.destinationName = pub_topic_control;
client.send(message);
}
}
// called when the client connects
function onConnect() {
console.log("onConnect");
client.subscribe(sub_topic);
document.getElementById("connectState").innerHTML = "MQTT Broker: Connected";
mqtt_broker_connected = true;
}
// called when the client loses its connection
function onConnectionLost(responseObject) {
if (responseObject.errorCode !== 0) {
console.log("onConnectionLost:" + responseObject.errorMessage);
document.getElementById("connectState").innerHTML = "MQTT Broker: Not connected";
alert("Connection lost to the MQTT broker. Refresh page to reconnect");
mqtt_broker_connected = false;
}
}
// called when a message arrives
function onMessageArrived(message) {
console.log("onMessageArrived:" + message.payloadString);
term.write(message.payloadString);
}
</script>
<title>ALTAIR 8800 Emulator</title>
</head>
<body onload="initTerminal();">
<div>
Enter your IoT Central device name
<input type="text" id="displayName" value="device name" style="width: 400px;">
<input type="button" value="Connect" onclick="connect();">
</div>
<br />
<div id="connectState">MQTT Broker: Not connected</div>
<div id="inputmode">Line input mode: Default (Ctrl+L to toggle)</div>
<div id="terminal"></div>
<script src="https://unpkg.com/[email protected]/lib/xterm.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.js" type="text/javascript"></script>
<script src="Javascript/xterm.js"></script>
<!-- <script src="Javascript/app.js"></script> -->
</body>
</html>