aprsd 1.0.0__py3-none-any.whl → 3.4.2__py3-none-any.whl

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.
Files changed (140) hide show
  1. aprsd/__init__.py +6 -4
  2. aprsd/cli_helper.py +151 -0
  3. aprsd/client/__init__.py +13 -0
  4. aprsd/client/aprsis.py +132 -0
  5. aprsd/client/base.py +105 -0
  6. aprsd/client/drivers/__init__.py +0 -0
  7. aprsd/client/drivers/aprsis.py +228 -0
  8. aprsd/client/drivers/fake.py +73 -0
  9. aprsd/client/drivers/kiss.py +119 -0
  10. aprsd/client/factory.py +88 -0
  11. aprsd/client/fake.py +48 -0
  12. aprsd/client/kiss.py +103 -0
  13. aprsd/client/stats.py +38 -0
  14. aprsd/cmds/__init__.py +0 -0
  15. aprsd/cmds/completion.py +22 -0
  16. aprsd/cmds/dev.py +162 -0
  17. aprsd/cmds/fetch_stats.py +156 -0
  18. aprsd/cmds/healthcheck.py +86 -0
  19. aprsd/cmds/list_plugins.py +319 -0
  20. aprsd/cmds/listen.py +231 -0
  21. aprsd/cmds/send_message.py +171 -0
  22. aprsd/cmds/server.py +137 -0
  23. aprsd/cmds/webchat.py +674 -0
  24. aprsd/conf/__init__.py +56 -0
  25. aprsd/conf/client.py +131 -0
  26. aprsd/conf/common.py +301 -0
  27. aprsd/conf/log.py +65 -0
  28. aprsd/conf/opts.py +80 -0
  29. aprsd/conf/plugin_common.py +182 -0
  30. aprsd/conf/plugin_email.py +105 -0
  31. aprsd/exception.py +13 -0
  32. aprsd/log/__init__.py +0 -0
  33. aprsd/log/log.py +138 -0
  34. aprsd/main.py +104 -867
  35. aprsd/packets/__init__.py +20 -0
  36. aprsd/packets/collector.py +79 -0
  37. aprsd/packets/core.py +823 -0
  38. aprsd/packets/log.py +161 -0
  39. aprsd/packets/packet_list.py +110 -0
  40. aprsd/packets/seen_list.py +49 -0
  41. aprsd/packets/tracker.py +103 -0
  42. aprsd/packets/watch_list.py +119 -0
  43. aprsd/plugin.py +474 -284
  44. aprsd/plugin_utils.py +86 -0
  45. aprsd/plugins/__init__.py +0 -0
  46. aprsd/plugins/email.py +709 -0
  47. aprsd/plugins/fortune.py +61 -0
  48. aprsd/plugins/location.py +179 -0
  49. aprsd/plugins/notify.py +61 -0
  50. aprsd/plugins/ping.py +31 -0
  51. aprsd/plugins/time.py +115 -0
  52. aprsd/plugins/version.py +31 -0
  53. aprsd/plugins/weather.py +405 -0
  54. aprsd/stats/__init__.py +20 -0
  55. aprsd/stats/app.py +49 -0
  56. aprsd/stats/collector.py +37 -0
  57. aprsd/threads/__init__.py +11 -0
  58. aprsd/threads/aprsd.py +119 -0
  59. aprsd/threads/keep_alive.py +131 -0
  60. aprsd/threads/log_monitor.py +121 -0
  61. aprsd/threads/registry.py +56 -0
  62. aprsd/threads/rx.py +354 -0
  63. aprsd/threads/stats.py +44 -0
  64. aprsd/threads/tx.py +255 -0
  65. aprsd/utils/__init__.py +218 -0
  66. aprsd/utils/counter.py +51 -0
  67. aprsd/utils/json.py +80 -0
  68. aprsd/utils/objectstore.py +123 -0
  69. aprsd/utils/ring_buffer.py +40 -0
  70. aprsd/utils/trace.py +180 -0
  71. aprsd/web/__init__.py +0 -0
  72. aprsd/web/admin/__init__.py +0 -0
  73. aprsd/web/admin/static/css/index.css +84 -0
  74. aprsd/web/admin/static/css/prism.css +4 -0
  75. aprsd/web/admin/static/css/tabs.css +35 -0
  76. aprsd/web/admin/static/images/Untitled.png +0 -0
  77. aprsd/web/admin/static/images/aprs-symbols-16-0.png +0 -0
  78. aprsd/web/admin/static/images/aprs-symbols-16-1.png +0 -0
  79. aprsd/web/admin/static/images/aprs-symbols-64-0.png +0 -0
  80. aprsd/web/admin/static/images/aprs-symbols-64-1.png +0 -0
  81. aprsd/web/admin/static/images/aprs-symbols-64-2.png +0 -0
  82. aprsd/web/admin/static/js/charts.js +235 -0
  83. aprsd/web/admin/static/js/echarts.js +465 -0
  84. aprsd/web/admin/static/js/logs.js +26 -0
  85. aprsd/web/admin/static/js/main.js +231 -0
  86. aprsd/web/admin/static/js/prism.js +12 -0
  87. aprsd/web/admin/static/js/send-message.js +114 -0
  88. aprsd/web/admin/static/js/tabs.js +28 -0
  89. aprsd/web/admin/templates/index.html +196 -0
  90. aprsd/web/chat/static/css/chat.css +115 -0
  91. aprsd/web/chat/static/css/index.css +66 -0
  92. aprsd/web/chat/static/css/style.css.map +1 -0
  93. aprsd/web/chat/static/css/tabs.css +41 -0
  94. aprsd/web/chat/static/css/upstream/bootstrap.min.css +6 -0
  95. aprsd/web/chat/static/css/upstream/font.woff2 +0 -0
  96. aprsd/web/chat/static/css/upstream/google-fonts.css +23 -0
  97. aprsd/web/chat/static/css/upstream/jquery-ui.css +1311 -0
  98. aprsd/web/chat/static/css/upstream/jquery.toast.css +28 -0
  99. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff +0 -0
  100. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff2 +0 -0
  101. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff +0 -0
  102. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff2 +0 -0
  103. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff +0 -0
  104. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff2 +0 -0
  105. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff +0 -0
  106. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff2 +0 -0
  107. aprsd/web/chat/static/images/Untitled.png +0 -0
  108. aprsd/web/chat/static/images/aprs-symbols-16-0.png +0 -0
  109. aprsd/web/chat/static/images/aprs-symbols-16-1.png +0 -0
  110. aprsd/web/chat/static/images/aprs-symbols-64-0.png +0 -0
  111. aprsd/web/chat/static/images/aprs-symbols-64-1.png +0 -0
  112. aprsd/web/chat/static/images/aprs-symbols-64-2.png +0 -0
  113. aprsd/web/chat/static/images/globe.svg +3 -0
  114. aprsd/web/chat/static/js/gps.js +84 -0
  115. aprsd/web/chat/static/js/main.js +45 -0
  116. aprsd/web/chat/static/js/send-message.js +585 -0
  117. aprsd/web/chat/static/js/tabs.js +28 -0
  118. aprsd/web/chat/static/js/upstream/bootstrap.bundle.min.js +7 -0
  119. aprsd/web/chat/static/js/upstream/jquery-3.7.1.min.js +2 -0
  120. aprsd/web/chat/static/js/upstream/jquery-ui.min.js +13 -0
  121. aprsd/web/chat/static/js/upstream/jquery.toast.js +374 -0
  122. aprsd/web/chat/static/js/upstream/semantic.min.js +11 -0
  123. aprsd/web/chat/static/js/upstream/socket.io.min.js +7 -0
  124. aprsd/web/chat/templates/index.html +139 -0
  125. aprsd/wsgi.py +315 -0
  126. aprsd-3.4.2.dist-info/AUTHORS +13 -0
  127. aprsd-3.4.2.dist-info/LICENSE +175 -0
  128. aprsd-3.4.2.dist-info/METADATA +793 -0
  129. aprsd-3.4.2.dist-info/RECORD +133 -0
  130. {aprsd-1.0.0.dist-info → aprsd-3.4.2.dist-info}/WHEEL +1 -1
  131. aprsd-3.4.2.dist-info/entry_points.txt +8 -0
  132. aprsd/fake_aprs.py +0 -83
  133. aprsd/utils.py +0 -166
  134. aprsd-1.0.0.dist-info/AUTHORS +0 -6
  135. aprsd-1.0.0.dist-info/METADATA +0 -181
  136. aprsd-1.0.0.dist-info/RECORD +0 -13
  137. aprsd-1.0.0.dist-info/entry_points.txt +0 -4
  138. aprsd-1.0.0.dist-info/pbr.json +0 -1
  139. /aprsd/{fuzzyclock.py → utils/fuzzyclock.py} +0 -0
  140. {aprsd-1.0.0.dist-info → aprsd-3.4.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,585 @@
1
+ var cleared = false;
2
+ var callsign_list = {};
3
+ var callsign_location = {};
4
+ var message_list = {};
5
+ var from_msg_list = {};
6
+ var selected_tab_callsign = null;
7
+ const socket = io("/sendmsg");
8
+
9
+ MSG_TYPE_TX = "tx";
10
+ MSG_TYPE_RX = "rx";
11
+ MSG_TYPE_ACK = "ack";
12
+
13
+ function reload_popovers() {
14
+ $('[data-bs-toggle="popover"]').popover(
15
+ {html: true, animation: true}
16
+ );
17
+ }
18
+
19
+ function build_location_string(msg) {
20
+ dt = new Date(parseInt(msg['lasttime']) * 1000);
21
+ loc = "Last Location Update: " + dt.toLocaleString();
22
+ loc += "<br>Latitude: " + msg['lat'] + "<br>Longitude: " + msg['lon'];
23
+ loc += "<br>" + "Altitude: " + msg['altitude'] + " m";
24
+ loc += "<br>" + "Speed: " + msg['speed'] + " kph";
25
+ loc += "<br>" + "Bearing: " + msg['course'] + "°";
26
+ loc += "<br>" + "distance: " + msg['distance'] + " km";
27
+ return loc;
28
+ }
29
+
30
+ function build_location_string_small(msg) {
31
+
32
+ dt = new Date(parseInt(msg['lasttime']) * 1000);
33
+
34
+ loc = "" + msg['distance'] + "km";
35
+ //loc += "Lat " + msg['lat'] + "&nbsp;Lon " + msg['lon'];
36
+ loc += "@" + msg['course'] + "°";
37
+ //loc += "&nbsp;Distance " + msg['distance'] + " km";
38
+ loc += "&nbsp;" + dt.toLocaleString();
39
+ return loc;
40
+ }
41
+
42
+ function size_dict(d){c=0; for (i in d) ++c; return c}
43
+
44
+ function raise_error(msg) {
45
+ $.toast({
46
+ heading: 'Error',
47
+ text: msg,
48
+ loader: true,
49
+ loaderBg: '#9EC600',
50
+ position: 'top-center',
51
+ });
52
+ }
53
+
54
+ function init_chat() {
55
+ socket.on('connect', function () {
56
+ console.log("Connected to socketio");
57
+ });
58
+ socket.on('connected', function(msg) {
59
+ console.log("Connected!");
60
+ console.log(msg);
61
+ });
62
+
63
+ socket.on("sent", function(msg) {
64
+ if (cleared === false) {
65
+ var msgsdiv = $("#msgsTabsDiv");
66
+ msgsdiv.html('');
67
+ cleared = true;
68
+ }
69
+ msg["type"] = MSG_TYPE_TX;
70
+ sent_msg(msg);
71
+ });
72
+
73
+ socket.on("ack", function(msg) {
74
+ msg["type"] = MSG_TYPE_ACK;
75
+ ack_msg(msg);
76
+ });
77
+
78
+ socket.on("new", function(msg) {
79
+ if (cleared === false) {
80
+ var msgsdiv = $("#msgsTabsDiv");
81
+ msgsdiv.html('')
82
+ cleared = true;
83
+ }
84
+ msg["type"] = MSG_TYPE_RX;
85
+ from_msg(msg);
86
+ });
87
+
88
+ socket.on("callsign_location", function(msg) {
89
+ console.log("CALLSIGN Location!");
90
+ console.log(msg);
91
+ now = new Date();
92
+ msg['last_updated'] = now;
93
+ callsign_location[msg['callsign']] = msg;
94
+
95
+ location_id = callsign_location_content(msg['callsign'], true);
96
+ location_string = build_location_string_small(msg);
97
+ $(location_id).html(location_string);
98
+ $(location_id+"Spinner").addClass('d-none');
99
+ save_data();
100
+ });
101
+
102
+ $("#sendform").submit(function(event) {
103
+ event.preventDefault();
104
+ to_call = $('#to_call').val();
105
+ message = $('#message').val();
106
+ path = $('#pkt_path option:selected').val();
107
+ if (to_call == "") {
108
+ raise_error("You must enter a callsign to send a message")
109
+ return false;
110
+ } else {
111
+ if (message == "") {
112
+ raise_error("You must enter a message to send")
113
+ return false;
114
+ }
115
+ msg = {'to': to_call, 'message': message, 'path': path};
116
+ //console.log(msg);
117
+ socket.emit("send", msg);
118
+ $('#message').val('');
119
+ }
120
+ });
121
+
122
+ init_gps();
123
+ // Try and load any existing chat threads from last time
124
+ init_messages();
125
+ }
126
+
127
+
128
+ function tab_string(callsign, id=false) {
129
+ name = "msgs"+callsign;
130
+ if (id) {
131
+ return "#"+name;
132
+ } else {
133
+ return name;
134
+ }
135
+ }
136
+
137
+ function tab_li_string(callsign, id=false) {
138
+ //The id of the LI containing the tab
139
+ return tab_string(callsign,id)+"Li";
140
+ }
141
+
142
+ function tab_notification_id(callsign, id=false) {
143
+ // The ID of the span that contains the notification count
144
+ return tab_string(callsign, id)+"notify";
145
+ }
146
+
147
+ function tab_content_name(callsign, id=false) {
148
+ return tab_string(callsign, id)+"Content";
149
+ }
150
+
151
+ function tab_content_speech_wrapper(callsign, id=false) {
152
+ return tab_string(callsign, id)+"SpeechWrapper";
153
+ }
154
+
155
+ function tab_content_speech_wrapper_id(callsign) {
156
+ return "#"+tab_content_speech_wrapper(callsign);
157
+ }
158
+
159
+ function content_divname(callsign) {
160
+ return "#"+tab_content_name(callsign);
161
+ }
162
+
163
+ function callsign_tab(callsign) {
164
+ return "#"+tab_string(callsign);
165
+ }
166
+
167
+ function callsign_location_popover(callsign, id=false) {
168
+ return tab_string(callsign, id)+"Location";
169
+ }
170
+
171
+ function callsign_location_content(callsign, id=false) {
172
+ return tab_string(callsign, id)+"LocationContent";
173
+ }
174
+
175
+ function bubble_msg_id(msg, id=false) {
176
+ // The id of the div that contains a specific message
177
+ name = msg["from_call"] + "_" + msg["msgNo"];
178
+ if (id) {
179
+ return "#"+name;
180
+ } else {
181
+ return name;
182
+ }
183
+ }
184
+
185
+ function message_ts_id(msg) {
186
+ //Create a 'id' from the message timestamp
187
+ ts_str = msg["timestamp"].toString();
188
+ ts = ts_str.split(".")[0]*1000;
189
+ id = ts_str.split('.')[0];
190
+ return {'timestamp': ts, 'id': id};
191
+ }
192
+
193
+ function time_ack_from_msg(msg) {
194
+ // Return the time and ack_id from a message
195
+ ts_id = message_ts_id(msg);
196
+ ts = ts_id['timestamp'];
197
+ id = ts_id['id'];
198
+ ack_id = "ack_" + id
199
+
200
+ var d = new Date(ts).toLocaleDateString("en-US")
201
+ var t = new Date(ts).toLocaleTimeString("en-US")
202
+ return {'time': t, 'date': d, 'ack_id': ack_id};
203
+ }
204
+
205
+ function save_data() {
206
+ // Save the relevant data to local storage
207
+ localStorage.setItem('callsign_list', JSON.stringify(callsign_list));
208
+ localStorage.setItem('message_list', JSON.stringify(message_list));
209
+ localStorage.setItem('callsign_location', JSON.stringify(callsign_location));
210
+ }
211
+
212
+ function init_messages() {
213
+ // This tries to load any previous conversations from local storage
214
+ callsign_list = JSON.parse(localStorage.getItem('callsign_list'));
215
+ message_list = JSON.parse(localStorage.getItem('message_list'));
216
+ callsign_location = JSON.parse(localStorage.getItem('callsign_location'));
217
+ if (callsign_list == null) {
218
+ callsign_list = {};
219
+ }
220
+ if (message_list == null) {
221
+ message_list = {};
222
+ }
223
+ if (callsign_location == null) {
224
+ callsign_location = {};
225
+ }
226
+ console.log(callsign_list);
227
+ console.log(message_list);
228
+ console.log(callsign_location);
229
+
230
+ // Now loop through each callsign and add the tabs
231
+ first_callsign = null;
232
+ for (callsign in callsign_list) {
233
+ if (first_callsign === null) {
234
+ first_callsign = callsign;
235
+ active = true;
236
+ } else {
237
+ active = false;
238
+ }
239
+ create_callsign_tab(callsign, active);
240
+ }
241
+ // and then populate the messages in order
242
+ for (callsign in message_list) {
243
+ new_callsign = true;
244
+ cleared = true;
245
+ for (id in message_list[callsign]) {
246
+ msg = message_list[callsign][id];
247
+ info = time_ack_from_msg(msg);
248
+ t = info['time'];
249
+ d = info['date'];
250
+ ack_id = false;
251
+ acked = false;
252
+ if (msg['type'] == MSG_TYPE_TX) {
253
+ ack_id = info['ack_id'];
254
+ acked = msg['ack'];
255
+ }
256
+ msg_html = create_message_html(d, t, msg['from_call'], msg['to_call'],
257
+ msg['message_text'], ack_id, msg, acked);
258
+ append_message_html(callsign, msg_html, new_callsign);
259
+ new_callsign = false;
260
+ }
261
+ }
262
+
263
+ if (first_callsign !== null) {
264
+ callsign_select(first_callsign);
265
+ }
266
+ }
267
+
268
+ function scroll_main_content(callsign=false) {
269
+ var wc = $('#wc-content');
270
+ var d = $('#msgsTabContent');
271
+ var scrollHeight = wc.prop('scrollHeight');
272
+ var clientHeight = wc.prop('clientHeight');
273
+
274
+ if (callsign) {
275
+ div_id = content_divname(callsign);
276
+ c_div = $(content_divname(callsign));
277
+ //console.log("c_div("+div_id+") " + c_div);
278
+ c_height = c_div.height();
279
+ c_scroll_height = c_div.prop('scrollHeight');
280
+ //console.log("callsign height " + c_height + " scrollHeight " + c_scroll_height);
281
+ if (c_height === undefined) {
282
+ return false;
283
+ }
284
+ if (c_height > clientHeight) {
285
+ wc.animate({ scrollTop: c_scroll_height }, 500);
286
+ } else {
287
+ wc.animate({ scrollTop: 0 }, 500);
288
+ }
289
+ } else {
290
+ if (scrollHeight > clientHeight) {
291
+ wc.animate({ scrollTop: wc.prop('scrollHeight') }, 500);
292
+ } else {
293
+ wc.animate({ scrollTop: 0 }, 500);
294
+ }
295
+ }
296
+ }
297
+
298
+ function create_callsign_tab(callsign, active=false) {
299
+ //Create the html for the callsign tab and insert it into the DOM
300
+ var callsignTabs = $("#msgsTabList");
301
+ tab_id = tab_string(callsign);
302
+ tab_id_li = tab_li_string(callsign);
303
+ tab_notify_id = tab_notification_id(callsign);
304
+ tab_content = tab_content_name(callsign);
305
+ popover_id = callsign_location_popover(callsign);
306
+ if (active) {
307
+ active_str = "active";
308
+ } else {
309
+ active_str = "";
310
+ }
311
+
312
+ item_html = '<li class="nav-item" role="presentation" callsign="'+callsign+'" id="'+tab_id_li+'">';
313
+ //item_html += '<button onClick="callsign_select(\''+callsign+'\');" callsign="'+callsign+'" class="nav-link '+active_str+'" id="'+tab_id+'" data-bs-toggle="tab" data-bs-target="#'+tab_content+'" type="button" role="tab" aria-controls="'+callsign+'" aria-selected="true">';
314
+ item_html += '<button onClick="callsign_select(\''+callsign+'\');" callsign="'+callsign+'" class="nav-link position-relative '+active_str+'" id="'+tab_id+'" data-bs-toggle="tab" data-bs-target="#'+tab_content+'" type="button" role="tab" aria-controls="'+callsign+'" aria-selected="true">';
315
+ item_html += callsign+'&nbsp;&nbsp;';
316
+ item_html += '<span id="'+tab_notify_id+'" class="position-absolute top-0 start-80 translate-middle badge bg-danger border border-light rounded-pill visually-hidden">0</span>';
317
+ item_html += '<span onclick="delete_tab(\''+callsign+'\');">×</span>';
318
+ item_html += '</button></li>'
319
+
320
+ callsignTabs.append(item_html);
321
+ create_callsign_tab_content(callsign, active);
322
+ }
323
+
324
+ function create_callsign_tab_content(callsign, active=false) {
325
+ var callsignTabsContent = $("#msgsTabContent");
326
+ tab_id = tab_string(callsign);
327
+ tab_content = tab_content_name(callsign);
328
+ wrapper_id = tab_content_speech_wrapper(callsign);
329
+ if (active) {
330
+ active_str = "show active";
331
+ } else {
332
+ active_str = '';
333
+ }
334
+
335
+ location_str = "Unknown Location"
336
+ if (callsign in callsign_location) {
337
+ location_str = build_location_string_small(callsign_location[callsign]);
338
+ location_class = '';
339
+ }
340
+
341
+ location_id = callsign_location_content(callsign);
342
+
343
+ item_html = '<div class="tab-pane fade '+active_str+'" id="'+tab_content+'" role="tabpanel" aria-labelledby="'+tab_id+'">';
344
+ item_html += '<div class="" style="border: 1px solid #999999;background-color:#aaaaaa;">';
345
+ item_html += '<div class="row" style="padding-top:4px;padding-bottom:4px;background-color:#aaaaaa;margin:0px;">';
346
+ item_html += '<div class="d-flex col-md-10 justify-content-left" style="padding:0px;margin:0px;">';
347
+ item_html += '<button onclick="call_callsign_location(\''+callsign+'\');" style="margin-left:2px;padding: 0px 4px 0px 4px;" type="button" class="btn btn-primary">';
348
+ item_html += '<span id="'+location_id+'Spinner" class="d-none spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>Update</button>';
349
+ item_html += '&nbsp;<span id="'+location_id+'">'+location_str+'</span></div>';
350
+ item_html += '</div>';
351
+ item_html += '<div class="speech-wrapper" id="'+wrapper_id+'"></div>';
352
+ item_html += '</div>';
353
+ callsignTabsContent.append(item_html);
354
+ }
355
+
356
+ function delete_tab(callsign) {
357
+ // User asked to delete the tab and the conversation
358
+ tab_id = tab_string(callsign, true);
359
+ tab_id_li = tab_li_string(callsign, true);
360
+ tab_content = tab_content_name(callsign, true);
361
+ $(tab_id_li).remove();
362
+ $(tab_content).remove();
363
+ delete callsign_list[callsign];
364
+ delete message_list[callsign];
365
+ delete callsign_location[callsign];
366
+
367
+ // Now select the first tab
368
+ first_tab = $("#msgsTabList").children().first().children().first();
369
+ console.log(first_tab);
370
+ $(first_tab).click();
371
+ save_data();
372
+ }
373
+
374
+ function add_callsign(callsign, msg) {
375
+ /* Ensure a callsign exists in the left hand nav */
376
+ if (callsign in callsign_list) {
377
+ return false
378
+ }
379
+ len = Object.keys(callsign_list).length;
380
+ if (len == 0) {
381
+ active = true;
382
+ } else {
383
+ active = false;
384
+ }
385
+ create_callsign_tab(callsign, active);
386
+ callsign_list[callsign] = '';
387
+ return true;
388
+ }
389
+
390
+ function update_callsign_path(callsign, msg) {
391
+ //Get the selected path to save for this callsign
392
+ path = msg['path']
393
+ $('#pkt_path').val(path);
394
+ callsign_list[callsign] = path;
395
+
396
+ }
397
+
398
+ function append_message(callsign, msg, msg_html) {
399
+ new_callsign = false
400
+ if (!message_list.hasOwnProperty(callsign)) {
401
+ message_list[callsign] = {};
402
+ }
403
+ ts_id = message_ts_id(msg);
404
+ id = ts_id['id']
405
+ message_list[callsign][id] = msg;
406
+ if (selected_tab_callsign != callsign) {
407
+ // We need to update the notification for the tab
408
+ tab_notify_id = tab_notification_id(callsign, true);
409
+ // get the current count of notifications
410
+ count = parseInt($(tab_notify_id).text());
411
+ if (isNaN(count)) {
412
+ count = 0;
413
+ }
414
+ count += 1;
415
+ $(tab_notify_id).text(count);
416
+ $(tab_notify_id).removeClass('visually-hidden');
417
+ }
418
+
419
+ // Find the right div to place the html
420
+ new_callsign = add_callsign(callsign, msg);
421
+ update_callsign_path(callsign, msg);
422
+ append_message_html(callsign, msg_html, new_callsign);
423
+ len = Object.keys(callsign_list).length;
424
+ if (new_callsign && len == 1) {
425
+ //Now click the tab if and only if there is only one tab
426
+ callsign_tab_id = callsign_tab(callsign);
427
+ $(callsign_tab_id).click();
428
+ callsign_select(callsign);
429
+ }
430
+ }
431
+
432
+
433
+ function append_message_html(callsign, msg_html, new_callsign) {
434
+ var msgsTabs = $('#msgsTabsDiv');
435
+ divname_str = tab_content_name(callsign);
436
+ divname = content_divname(callsign);
437
+ tab_content = tab_content_name(callsign);
438
+ wrapper_id = tab_content_speech_wrapper_id(callsign);
439
+
440
+ $(wrapper_id).append(msg_html);
441
+
442
+ if ($(wrapper_id).children().length > 0) {
443
+ $(wrapper_id).animate({scrollTop: $(wrapper_id)[0].scrollHeight}, "fast");
444
+ }
445
+ }
446
+
447
+ function create_message_html(date, time, from, to, message, ack_id, msg, acked=false) {
448
+ div_id = from + "_" + msg.msgNo;
449
+ if (ack_id) {
450
+ alt = " alt"
451
+ } else {
452
+ alt = ""
453
+ }
454
+
455
+ bubble_class = "bubble" + alt + " text-nowrap"
456
+ bubble_name_class = "bubble-name" + alt
457
+ bubble_msgid = bubble_msg_id(msg);
458
+ date_str = date + " " + time;
459
+ sane_date_str = date_str.replace(/ /g,"").replaceAll("/","").replaceAll(":","");
460
+
461
+ bubble_msg_class = "bubble-message";
462
+ if (ack_id) {
463
+ bubble_arrow_class = "bubble-arrow alt";
464
+ popover_placement = "left";
465
+ } else {
466
+ bubble_arrow_class = "bubble-arrow";
467
+ popover_placement = "right";
468
+ }
469
+
470
+ msg_html = '<div class="bubble-row'+alt+'">';
471
+ msg_html += '<div id="'+bubble_msgid+'" class="'+ bubble_class + '" ';
472
+ msg_html += 'title="APRS Raw Packet" data-bs-placement="'+popover_placement+'" data-bs-toggle="popover" ';
473
+ msg_html += 'data-bs-trigger="hover" data-bs-content="'+msg['raw']+'">';
474
+ msg_html += '<div class="bubble-text">';
475
+ msg_html += '<p class="'+ bubble_name_class +'">'+from+'&nbsp;&nbsp;';
476
+ msg_html += '<span class="bubble-timestamp">'+date_str+'</span>';
477
+
478
+ if (ack_id) {
479
+ if (acked) {
480
+ msg_html += '<span class="material-symbols-rounded md-10" id="' + ack_id + '">thumb_up</span>';
481
+ } else {
482
+ msg_html += '<span class="material-symbols-rounded md-10" id="' + ack_id + '">thumb_down</span>';
483
+ }
484
+ }
485
+ msg_html += "</p>";
486
+ msg_html += '<p class="' +bubble_msg_class+ '">'+message+'</p>';
487
+ msg_html += '<div class="'+ bubble_arrow_class + '"></div>';
488
+ msg_html += "</div></div></div>";
489
+
490
+ return msg_html
491
+ }
492
+
493
+ function flash_message(msg) {
494
+ // Callback function to bring a hidden box back
495
+ msg_id = bubble_msg_id(msg, true);
496
+ $(msg_id).fadeOut(100).fadeIn(100).fadeOut(100).fadeIn(100).fadeOut(100).fadeIn(100);
497
+ }
498
+
499
+ function sent_msg(msg) {
500
+ info = time_ack_from_msg(msg);
501
+ t = info['time'];
502
+ d = info['date'];
503
+ ack_id = info['ack_id'];
504
+
505
+ msg_html = create_message_html(d, t, msg['from_call'], msg['to_call'], msg['message_text'], ack_id, msg, false);
506
+ append_message(msg['to_call'], msg, msg_html);
507
+ save_data();
508
+ scroll_main_content(msg['to_call']);
509
+ }
510
+
511
+ function from_msg(msg) {
512
+ if (!from_msg_list.hasOwnProperty(msg["from_call"])) {
513
+ from_msg_list[msg["from_call"]] = new Array();
514
+ }
515
+
516
+ if (msg["msgNo"] in from_msg_list[msg["from_call"]]) {
517
+ // We already have this message
518
+ //console.log("We already have this message msgNo=" + msg["msgNo"]);
519
+ // Do some flashy thing?
520
+ flash_message(msg);
521
+ return false
522
+ } else {
523
+ from_msg_list[msg["from_call"]][msg["msgNo"]] = msg
524
+ }
525
+ info = time_ack_from_msg(msg);
526
+ t = info['time'];
527
+ d = info['date'];
528
+ ack_id = info['ack_id'];
529
+
530
+ from = msg['from_call']
531
+ msg_html = create_message_html(d, t, from, false, msg['message_text'], false, msg, false);
532
+ append_message(from, msg, msg_html);
533
+ save_data();
534
+ scroll_main_content(from);
535
+ }
536
+
537
+ function ack_msg(msg) {
538
+ // Acknowledge a message
539
+ // We have an existing entry
540
+ ts_id = message_ts_id(msg);
541
+ id = ts_id['id'];
542
+ //Mark the message as acked
543
+ callsign = msg['to_call'];
544
+ // Ensure the message_list has this callsign
545
+ if (!message_list.hasOwnProperty(callsign)) {
546
+ return false
547
+ }
548
+ // Ensure the message_list has this id
549
+ if (!message_list[callsign].hasOwnProperty(id)) {
550
+ return false
551
+ }
552
+ if (message_list[callsign][id]['ack'] == true) {
553
+ return false;
554
+ }
555
+ message_list[callsign][id]['ack'] = true;
556
+ ack_id = "ack_" + id
557
+
558
+ if (msg['ack'] == true) {
559
+ var ack_div = $('#' + ack_id);
560
+ ack_div.html('thumb_up');
561
+ }
562
+
563
+ //$('.ui.accordion').accordion('refresh');
564
+ save_data();
565
+ scroll_main_content();
566
+ }
567
+
568
+ function callsign_select(callsign) {
569
+ var tocall = $("#to_call");
570
+ tocall.val(callsign);
571
+ scroll_main_content(callsign);
572
+ selected_tab_callsign = callsign;
573
+ tab_notify_id = tab_notification_id(callsign, true);
574
+ $(tab_notify_id).addClass('visually-hidden');
575
+ $(tab_notify_id).text(0);
576
+ // Now update the path
577
+ $('#pkt_path').val(callsign_list[callsign]);
578
+ }
579
+
580
+ function call_callsign_location(callsign) {
581
+ msg = {'callsign': callsign};
582
+ socket.emit("get_callsign_location", msg);
583
+ location_id = callsign_location_content(callsign, true)+"Spinner";
584
+ $(location_id).removeClass('d-none');
585
+ }
@@ -0,0 +1,28 @@
1
+ function openTab(evt, tabName) {
2
+ // Declare all variables
3
+ var i, tabcontent, tablinks;
4
+
5
+ if (typeof tabName == 'undefined') {
6
+ return
7
+ }
8
+
9
+ // Get all elements with class="tabcontent" and hide them
10
+ tabcontent = document.getElementsByClassName("tabcontent");
11
+ for (i = 0; i < tabcontent.length; i++) {
12
+ tabcontent[i].style.display = "none";
13
+ }
14
+
15
+ // Get all elements with class="tablinks" and remove the class "active"
16
+ tablinks = document.getElementsByClassName("tablinks");
17
+ for (i = 0; i < tablinks.length; i++) {
18
+ tablinks[i].className = tablinks[i].className.replace(" active", "");
19
+ }
20
+
21
+ // Show the current tab, and add an "active" class to the button that opened the tab
22
+ document.getElementById(tabName).style.display = "block";
23
+ if (typeof evt.currentTarget == 'undefined') {
24
+ return
25
+ } else {
26
+ evt.currentTarget.className += " active";
27
+ }
28
+ }