aprsd 3.4.4__py3-none-any.whl → 4.0.1__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 (114) hide show
  1. aprsd/cli_helper.py +12 -5
  2. aprsd/client/aprsis.py +31 -9
  3. aprsd/client/base.py +13 -2
  4. aprsd/client/drivers/aprsis.py +6 -3
  5. aprsd/client/drivers/fake.py +15 -20
  6. aprsd/client/factory.py +0 -2
  7. aprsd/client/fake.py +0 -2
  8. aprsd/client/kiss.py +17 -2
  9. aprsd/client/stats.py +1 -3
  10. aprsd/cmds/completion.py +7 -4
  11. aprsd/cmds/dev.py +38 -42
  12. aprsd/cmds/fetch_stats.py +140 -143
  13. aprsd/cmds/healthcheck.py +5 -3
  14. aprsd/cmds/list_plugins.py +140 -134
  15. aprsd/cmds/listen.py +13 -9
  16. aprsd/cmds/server.py +53 -27
  17. aprsd/conf/__init__.py +1 -2
  18. aprsd/conf/client.py +3 -4
  19. aprsd/conf/common.py +19 -93
  20. aprsd/conf/log.py +2 -4
  21. aprsd/conf/opts.py +5 -4
  22. aprsd/conf/plugin_common.py +11 -121
  23. aprsd/exception.py +2 -0
  24. aprsd/log/log.py +7 -46
  25. aprsd/main.py +10 -4
  26. aprsd/packets/__init__.py +14 -4
  27. aprsd/packets/core.py +57 -67
  28. aprsd/packets/log.py +8 -8
  29. aprsd/packets/packet_list.py +9 -6
  30. aprsd/plugin.py +22 -11
  31. aprsd/plugins/notify.py +1 -4
  32. aprsd/plugins/weather.py +10 -8
  33. aprsd/stats/collector.py +5 -2
  34. aprsd/threads/__init__.py +3 -2
  35. aprsd/threads/aprsd.py +12 -7
  36. aprsd/threads/{keep_alive.py → keepalive.py} +14 -45
  37. aprsd/threads/registry.py +3 -3
  38. aprsd/threads/rx.py +9 -6
  39. aprsd/threads/stats.py +2 -2
  40. aprsd/threads/tx.py +3 -4
  41. aprsd/utils/__init__.py +42 -10
  42. aprsd/utils/json.py +9 -4
  43. aprsd/utils/keepalive_collector.py +55 -0
  44. aprsd/utils/trace.py +0 -2
  45. aprsd-4.0.1.dist-info/AUTHORS +1 -0
  46. {aprsd-3.4.4.dist-info → aprsd-4.0.1.dist-info}/METADATA +307 -408
  47. aprsd-4.0.1.dist-info/RECORD +74 -0
  48. {aprsd-3.4.4.dist-info → aprsd-4.0.1.dist-info}/WHEEL +1 -1
  49. aprsd/cmds/admin.py +0 -57
  50. aprsd/cmds/webchat.py +0 -662
  51. aprsd/conf/plugin_email.py +0 -105
  52. aprsd/plugins/email.py +0 -715
  53. aprsd/plugins/location.py +0 -181
  54. aprsd/threads/log_monitor.py +0 -121
  55. aprsd/web/__init__.py +0 -0
  56. aprsd/web/admin/__init__.py +0 -0
  57. aprsd/web/admin/static/css/index.css +0 -84
  58. aprsd/web/admin/static/css/prism.css +0 -4
  59. aprsd/web/admin/static/css/tabs.css +0 -35
  60. aprsd/web/admin/static/images/Untitled.png +0 -0
  61. aprsd/web/admin/static/images/aprs-symbols-16-0.png +0 -0
  62. aprsd/web/admin/static/images/aprs-symbols-16-1.png +0 -0
  63. aprsd/web/admin/static/images/aprs-symbols-64-0.png +0 -0
  64. aprsd/web/admin/static/images/aprs-symbols-64-1.png +0 -0
  65. aprsd/web/admin/static/images/aprs-symbols-64-2.png +0 -0
  66. aprsd/web/admin/static/js/charts.js +0 -235
  67. aprsd/web/admin/static/js/echarts.js +0 -465
  68. aprsd/web/admin/static/js/logs.js +0 -26
  69. aprsd/web/admin/static/js/main.js +0 -231
  70. aprsd/web/admin/static/js/prism.js +0 -12
  71. aprsd/web/admin/static/js/send-message.js +0 -114
  72. aprsd/web/admin/static/js/tabs.js +0 -28
  73. aprsd/web/admin/templates/index.html +0 -196
  74. aprsd/web/chat/static/css/chat.css +0 -115
  75. aprsd/web/chat/static/css/index.css +0 -66
  76. aprsd/web/chat/static/css/style.css.map +0 -1
  77. aprsd/web/chat/static/css/tabs.css +0 -41
  78. aprsd/web/chat/static/css/upstream/bootstrap.min.css +0 -6
  79. aprsd/web/chat/static/css/upstream/font.woff2 +0 -0
  80. aprsd/web/chat/static/css/upstream/google-fonts.css +0 -23
  81. aprsd/web/chat/static/css/upstream/jquery-ui.css +0 -1311
  82. aprsd/web/chat/static/css/upstream/jquery.toast.css +0 -28
  83. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff +0 -0
  84. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff2 +0 -0
  85. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff +0 -0
  86. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff2 +0 -0
  87. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff +0 -0
  88. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff2 +0 -0
  89. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff +0 -0
  90. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff2 +0 -0
  91. aprsd/web/chat/static/images/Untitled.png +0 -0
  92. aprsd/web/chat/static/images/aprs-symbols-16-0.png +0 -0
  93. aprsd/web/chat/static/images/aprs-symbols-16-1.png +0 -0
  94. aprsd/web/chat/static/images/aprs-symbols-64-0.png +0 -0
  95. aprsd/web/chat/static/images/aprs-symbols-64-1.png +0 -0
  96. aprsd/web/chat/static/images/aprs-symbols-64-2.png +0 -0
  97. aprsd/web/chat/static/images/globe.svg +0 -3
  98. aprsd/web/chat/static/js/gps.js +0 -84
  99. aprsd/web/chat/static/js/main.js +0 -45
  100. aprsd/web/chat/static/js/send-message.js +0 -612
  101. aprsd/web/chat/static/js/tabs.js +0 -28
  102. aprsd/web/chat/static/js/upstream/bootstrap.bundle.min.js +0 -7
  103. aprsd/web/chat/static/js/upstream/jquery-3.7.1.min.js +0 -2
  104. aprsd/web/chat/static/js/upstream/jquery-ui.min.js +0 -13
  105. aprsd/web/chat/static/js/upstream/jquery.toast.js +0 -374
  106. aprsd/web/chat/static/js/upstream/semantic.min.js +0 -11
  107. aprsd/web/chat/static/js/upstream/socket.io.min.js +0 -7
  108. aprsd/web/chat/templates/index.html +0 -139
  109. aprsd/wsgi.py +0 -322
  110. aprsd-3.4.4.dist-info/AUTHORS +0 -13
  111. aprsd-3.4.4.dist-info/RECORD +0 -134
  112. {aprsd-3.4.4.dist-info → aprsd-4.0.1.dist-info}/LICENSE +0 -0
  113. {aprsd-3.4.4.dist-info → aprsd-4.0.1.dist-info}/entry_points.txt +0 -0
  114. {aprsd-3.4.4.dist-info → aprsd-4.0.1.dist-info}/top_level.txt +0 -0
@@ -1,231 +0,0 @@
1
- // watchlist is a dict of ham callsign => symbol, packets
2
- var watchlist = {};
3
- var our_callsign = "";
4
-
5
- function aprs_img(item, x_offset, y_offset) {
6
- var x = x_offset * -16;
7
- if (y_offset > 5) {
8
- y_offset = 5;
9
- }
10
- var y = y_offset * -16;
11
- var loc = x + 'px '+ y + 'px'
12
- item.css('background-position', loc);
13
- }
14
-
15
- function show_aprs_icon(item, symbol) {
16
- var offset = ord(symbol) - 33;
17
- var col = Math.floor(offset / 16);
18
- var row = offset % 16;
19
- //console.log("'" + symbol+"' off: "+offset+" row: "+ row + " col: " + col)
20
- aprs_img(item, row, col);
21
- }
22
-
23
- function ord(str){return str.charCodeAt(0);}
24
-
25
-
26
- function update_watchlist( data ) {
27
- // Update the watch list
28
- stats = data["stats"];
29
- if (stats.hasOwnProperty("WatchList") == false) {
30
- return
31
- }
32
- var watchdiv = $("#watchDiv");
33
- var html_str = '<table class="ui celled striped table"><thead><tr><th>HAM Callsign</th><th>Age since last seen by APRSD</th></tr></thead><tbody>'
34
- watchdiv.html('')
35
- jQuery.each(stats["WatchList"], function(i, val) {
36
- html_str += '<tr><td class="collapsing"><img id="callsign_'+i+'" class="aprsd_1"></img>' + i + '</td><td>' + val["last"] + '</td></tr>'
37
- });
38
- html_str += "</tbody></table>";
39
- watchdiv.append(html_str);
40
-
41
- jQuery.each(watchlist, function(i, val) {
42
- //update the symbol
43
- var call_img = $('#callsign_'+i);
44
- show_aprs_icon(call_img, val['symbol'])
45
- });
46
- }
47
-
48
- function update_watchlist_from_packet(callsign, val) {
49
- if (!watchlist.hasOwnProperty(callsign)) {
50
- watchlist[callsign] = {
51
- "symbol": '[',
52
- "packets": {},
53
- }
54
- } else {
55
- if (val.hasOwnProperty('symbol')) {
56
- //console.log("Updating symbol for "+callsign + " to "+val["symbol"])
57
- watchlist[callsign]["symbol"] = val["symbol"]
58
- }
59
- }
60
- if (watchlist[callsign]["packets"].hasOwnProperty(val['ts']) == false) {
61
- watchlist[callsign]["packets"][val['ts']]= val;
62
- }
63
- //console.log(watchlist)
64
- }
65
-
66
- function update_seenlist( data ) {
67
- stats = data["stats"];
68
- if (stats.hasOwnProperty("SeenList") == false) {
69
- return
70
- }
71
- var seendiv = $("#seenDiv");
72
- var html_str = '<table class="ui celled striped table">'
73
- html_str += '<thead><tr><th>HAM Callsign</th><th>Age since last seen by APRSD</th>'
74
- html_str += '<th>Number of packets RX</th></tr></thead><tbody>'
75
- seendiv.html('')
76
- var seen_list = stats["SeenList"]
77
- var len = Object.keys(seen_list).length
78
- $('#seen_count').html(len)
79
- jQuery.each(seen_list, function(i, val) {
80
- html_str += '<tr><td class="collapsing">'
81
- html_str += '<img id="callsign_'+i+'" class="aprsd_1"></img>' + i + '</td>'
82
- html_str += '<td>' + val["last"] + '</td>'
83
- html_str += '<td>' + val["count"] + '</td></tr>'
84
- });
85
- html_str += "</tbody></table>";
86
- seendiv.append(html_str);
87
- }
88
-
89
- function update_plugins( data ) {
90
- stats = data["stats"];
91
- if (stats.hasOwnProperty("PluginManager") == false) {
92
- return
93
- }
94
- var plugindiv = $("#pluginDiv");
95
- var html_str = '<table class="ui celled striped table"><thead><tr>'
96
- html_str += '<th>Plugin Name</th><th>Plugin Enabled?</th>'
97
- html_str += '<th>Processed Packets</th><th>Sent Packets</th>'
98
- html_str += '<th>Version</th>'
99
- html_str += '</tr></thead><tbody>'
100
- plugindiv.html('')
101
-
102
- var plugins = stats["PluginManager"];
103
- var keys = Object.keys(plugins);
104
- keys.sort();
105
- for (var i=0; i<keys.length; i++) { // now lets iterate in sort order
106
- var key = keys[i];
107
- var val = plugins[key];
108
- html_str += '<tr><td class="collapsing">' + key + '</td>';
109
- html_str += '<td>' + val["enabled"] + '</td><td>' + val["rx"] + '</td>';
110
- html_str += '<td>' + val["tx"] + '</td><td>' + val["version"] +'</td></tr>';
111
- }
112
- html_str += "</tbody></table>";
113
- plugindiv.append(html_str);
114
- }
115
-
116
- function update_threads( data ) {
117
- stats = data["stats"];
118
- if (stats.hasOwnProperty("APRSDThreadList") == false) {
119
- return
120
- }
121
- var threadsdiv = $("#threadsDiv");
122
- var countdiv = $("#thread_count");
123
- var html_str = '<table class="ui celled striped table"><thead><tr>'
124
- html_str += '<th>Thread Name</th><th>Alive?</th>'
125
- html_str += '<th>Age</th><th>Loop Count</th>'
126
- html_str += '</tr></thead><tbody>'
127
- threadsdiv.html('')
128
-
129
- var threads = stats["APRSDThreadList"];
130
- var keys = Object.keys(threads);
131
- countdiv.html(keys.length);
132
- keys.sort();
133
- for (var i=0; i<keys.length; i++) { // now lets iterate in sort order
134
- var key = keys[i];
135
- var val = threads[key];
136
- html_str += '<tr><td class="collapsing">' + key + '</td>';
137
- html_str += '<td>' + val["alive"] + '</td><td>' + val["age"] + '</td>';
138
- html_str += '<td>' + val["loop_count"] + '</td></tr>';
139
- }
140
- html_str += "</tbody></table>";
141
- threadsdiv.append(html_str);
142
- }
143
-
144
- function update_packets( data ) {
145
- var packetsdiv = $("#packetsDiv");
146
- //nuke the contents first, then add to it.
147
- if (size_dict(packet_list) == 0 && size_dict(data) > 0) {
148
- packetsdiv.html('')
149
- }
150
- jQuery.each(data.packets, function(i, val) {
151
- pkt = val;
152
-
153
- update_watchlist_from_packet(pkt['from_call'], pkt);
154
- if ( packet_list.hasOwnProperty(pkt['timestamp']) == false ) {
155
- // Store the packet
156
- packet_list[pkt['timestamp']] = pkt;
157
- //ts_str = val["timestamp"].toString();
158
- //ts = ts_str.split(".")[0]*1000;
159
- ts = pkt['timestamp'] * 1000;
160
- var d = new Date(ts).toLocaleDateString();
161
- var t = new Date(ts).toLocaleTimeString();
162
- var from_call = pkt.from_call;
163
- if (from_call == our_callsign) {
164
- title_id = 'title_tx';
165
- } else {
166
- title_id = 'title_rx';
167
- }
168
- var from_to = d + " " + t + "&nbsp;&nbsp;&nbsp;&nbsp;" + from_call + " > "
169
-
170
- if (val.hasOwnProperty('addresse')) {
171
- from_to = from_to + pkt['addresse']
172
- } else if (pkt.hasOwnProperty('to_call')) {
173
- from_to = from_to + pkt['to_call']
174
- } else if (pkt.hasOwnProperty('format') && pkt['format'] == 'mic-e') {
175
- from_to = from_to + "Mic-E"
176
- }
177
-
178
- from_to = from_to + "&nbsp;&nbsp;-&nbsp;&nbsp;" + pkt['raw']
179
-
180
- json_pretty = Prism.highlight(JSON.stringify(pkt, null, '\t'), Prism.languages.json, 'json');
181
- pkt_html = '<div class="title" id="' + title_id + '"><i class="dropdown icon"></i>' + from_to + '</div><div class="content"><p class="transition hidden"><pre class="language-json">' + json_pretty + '</p></p></div>'
182
- packetsdiv.prepend(pkt_html);
183
- }
184
- });
185
-
186
- $('.ui.accordion').accordion('refresh');
187
-
188
- // Update the count of messages shown
189
- cnt = size_dict(packet_list);
190
- //console.log("packets list " + cnt)
191
- $('#packets_count').html(cnt);
192
-
193
- const html_pretty = Prism.highlight(JSON.stringify(data, null, '\t'), Prism.languages.json, 'json');
194
- $("#packetsjson").html(html_pretty);
195
- }
196
-
197
-
198
- function start_update() {
199
-
200
- (function statsworker() {
201
- $.ajax({
202
- url: "/stats",
203
- type: 'GET',
204
- dataType: 'json',
205
- success: function(data) {
206
- update_stats(data);
207
- update_watchlist(data);
208
- update_seenlist(data);
209
- update_plugins(data);
210
- update_threads(data);
211
- },
212
- complete: function() {
213
- setTimeout(statsworker, 10000);
214
- }
215
- });
216
- })();
217
-
218
- (function packetsworker() {
219
- $.ajax({
220
- url: "/packets",
221
- type: 'GET',
222
- dataType: 'json',
223
- success: function(data) {
224
- update_packets(data);
225
- },
226
- complete: function() {
227
- setTimeout(packetsworker, 10000);
228
- }
229
- });
230
- })();
231
- }
@@ -1,12 +0,0 @@
1
- /* PrismJS 1.29.0
2
- https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript+json+json5+log&plugins=show-language+toolbar */
3
- var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function e(n,t){var r,i;switch(t=t||{},a.util.type(n)){case"Object":if(i=a.util.objId(n),t[i])return t[i];for(var l in r={},t[i]=r,n)n.hasOwnProperty(l)&&(r[l]=e(n[l],t));return r;case"Array":return i=a.util.objId(n),t[i]?t[i]:(r=[],t[i]=r,n.forEach((function(n,a){r[a]=e(n,t)})),r);default:return n}},getLanguage:function(e){for(;e;){var t=n.exec(e.className);if(t)return t[1].toLowerCase();e=e.parentElement}return"none"},setLanguage:function(e,t){e.className=e.className.replace(RegExp(n,"gi"),""),e.classList.add("language-"+t)},currentScript:function(){if("undefined"==typeof document)return null;if("currentScript"in document)return document.currentScript;try{throw new Error}catch(r){var e=(/at [^(\r\n]*\((.*):[^:]+:[^:]+\)$/i.exec(r.stack)||[])[1];if(e){var n=document.getElementsByTagName("script");for(var t in n)if(n[t].src==e)return n[t]}return null}},isActive:function(e,n,t){for(var r="no-"+n;e;){var a=e.classList;if(a.contains(n))return!0;if(a.contains(r))return!1;e=e.parentElement}return!!t}},languages:{plain:r,plaintext:r,text:r,txt:r,extend:function(e,n){var t=a.util.clone(a.languages[e]);for(var r in n)t[r]=n[r];return t},insertBefore:function(e,n,t,r){var i=(r=r||a.languages)[e],l={};for(var o in i)if(i.hasOwnProperty(o)){if(o==n)for(var s in t)t.hasOwnProperty(s)&&(l[s]=t[s]);t.hasOwnProperty(o)||(l[o]=i[o])}var u=r[e];return r[e]=l,a.languages.DFS(a.languages,(function(n,t){t===u&&n!=e&&(this[n]=l)})),l},DFS:function e(n,t,r,i){i=i||{};var l=a.util.objId;for(var o in n)if(n.hasOwnProperty(o)){t.call(n,o,n[o],r||o);var s=n[o],u=a.util.type(s);"Object"!==u||i[l(s)]?"Array"!==u||i[l(s)]||(i[l(s)]=!0,e(s,t,o,i)):(i[l(s)]=!0,e(s,t,null,i))}}},plugins:{},highlightAll:function(e,n){a.highlightAllUnder(document,e,n)},highlightAllUnder:function(e,n,t){var r={callback:t,container:e,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};a.hooks.run("before-highlightall",r),r.elements=Array.prototype.slice.apply(r.container.querySelectorAll(r.selector)),a.hooks.run("before-all-elements-highlight",r);for(var i,l=0;i=r.elements[l++];)a.highlightElement(i,!0===n,r.callback)},highlightElement:function(n,t,r){var i=a.util.getLanguage(n),l=a.languages[i];a.util.setLanguage(n,i);var o=n.parentElement;o&&"pre"===o.nodeName.toLowerCase()&&a.util.setLanguage(o,i);var s={element:n,language:i,grammar:l,code:n.textContent};function u(e){s.highlightedCode=e,a.hooks.run("before-insert",s),s.element.innerHTML=s.highlightedCode,a.hooks.run("after-highlight",s),a.hooks.run("complete",s),r&&r.call(s.element)}if(a.hooks.run("before-sanity-check",s),(o=s.element.parentElement)&&"pre"===o.nodeName.toLowerCase()&&!o.hasAttribute("tabindex")&&o.setAttribute("tabindex","0"),!s.code)return a.hooks.run("complete",s),void(r&&r.call(s.element));if(a.hooks.run("before-highlight",s),s.grammar)if(t&&e.Worker){var c=new Worker(a.filename);c.onmessage=function(e){u(e.data)},c.postMessage(JSON.stringify({language:s.language,code:s.code,immediateClose:!0}))}else u(a.highlight(s.code,s.grammar,s.language));else u(a.util.encode(s.code))},highlight:function(e,n,t){var r={code:e,grammar:n,language:t};if(a.hooks.run("before-tokenize",r),!r.grammar)throw new Error('The language "'+r.language+'" has no grammar.');return r.tokens=a.tokenize(r.code,r.grammar),a.hooks.run("after-tokenize",r),i.stringify(a.util.encode(r.tokens),r.language)},tokenize:function(e,n){var t=n.rest;if(t){for(var r in t)n[r]=t[r];delete n.rest}var a=new s;return u(a,a.head,e),o(e,a,n,a.head,0),function(e){for(var n=[],t=e.head.next;t!==e.tail;)n.push(t.value),t=t.next;return n}(a)},hooks:{all:{},add:function(e,n){var t=a.hooks.all;t[e]=t[e]||[],t[e].push(n)},run:function(e,n){var t=a.hooks.all[e];if(t&&t.length)for(var r,i=0;r=t[i++];)r(n)}},Token:i};function i(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=0|(r||"").length}function l(e,n,t,r){e.lastIndex=n;var a=e.exec(t);if(a&&r&&a[1]){var i=a[1].length;a.index+=i,a[0]=a[0].slice(i)}return a}function o(e,n,t,r,s,g){for(var f in t)if(t.hasOwnProperty(f)&&t[f]){var h=t[f];h=Array.isArray(h)?h:[h];for(var d=0;d<h.length;++d){if(g&&g.cause==f+","+d)return;var v=h[d],p=v.inside,m=!!v.lookbehind,y=!!v.greedy,k=v.alias;if(y&&!v.pattern.global){var x=v.pattern.toString().match(/[imsuy]*$/)[0];v.pattern=RegExp(v.pattern.source,x+"g")}for(var b=v.pattern||v,w=r.next,A=s;w!==n.tail&&!(g&&A>=g.reach);A+=w.value.length,w=w.next){var E=w.value;if(n.length>e.length)return;if(!(E instanceof i)){var P,L=1;if(y){if(!(P=l(b,A,e,m))||P.index>=e.length)break;var S=P.index,O=P.index+P[0].length,j=A;for(j+=w.value.length;S>=j;)j+=(w=w.next).value.length;if(A=j-=w.value.length,w.value instanceof i)continue;for(var C=w;C!==n.tail&&(j<O||"string"==typeof C.value);C=C.next)L++,j+=C.value.length;L--,E=e.slice(A,j),P.index-=A}else if(!(P=l(b,0,E,m)))continue;S=P.index;var N=P[0],_=E.slice(0,S),M=E.slice(S+N.length),W=A+E.length;g&&W>g.reach&&(g.reach=W);var z=w.prev;if(_&&(z=u(n,z,_),A+=_.length),c(n,z,L),w=u(n,z,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),L>1){var I={cause:f+","+d,reach:W};o(e,n,t,w.prev,A,I),g&&I.reach>g.reach&&(g.reach=I.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a<t&&r!==e.tail;a++)r=r.next;n.next=r,r.prev=n,e.length-=a}if(e.Prism=a,i.stringify=function e(n,t){if("string"==typeof n)return n;if(Array.isArray(n)){var r="";return n.forEach((function(n){r+=e(n,t)})),r}var i={type:n.type,content:e(n.content,t),tag:"span",classes:["token",n.type],attributes:{},language:t},l=n.alias;l&&(Array.isArray(l)?Array.prototype.push.apply(i.classes,l):i.classes.push(l)),a.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=" "+s+'="'+(i.attributes[s]||"").replace(/"/g,"&quot;")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+o+">"+i.content+"</"+i.tag+">"},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener("message",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute("data-manual")&&(a.manual=!0)),!a.manual){var h=document.readyState;"loading"===h||"interactive"===h&&g&&g.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
4
- Prism.languages.markup={comment:{pattern:/<!--(?:(?!<!--)[\s\S])*?-->/,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^<!|>$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup,Prism.hooks.add("wrap",(function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&amp;/,"&"))})),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^<!\[CDATA\[|\]\]>$/i;var t={"included-cdata":{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,inside:s}};t["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var n={};n[a]={pattern:RegExp("(<__[^>]*>)(?:<!\\[CDATA\\[(?:[^\\]]|\\](?!\\]>))*\\]\\]>|(?!<!\\[CDATA\\[)[^])*?(?=</__>)".replace(/__/g,(function(){return a})),"i"),lookbehind:!0,greedy:!0,inside:t},Prism.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(Prism.languages.markup.tag,"addAttribute",{value:function(a,e){Prism.languages.markup.tag.inside["special-attr"].push({pattern:RegExp("(^|[\"'\\s])(?:"+a+")\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))","i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[e,"language-"+e],inside:Prism.languages[e]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml;
5
- !function(s){var e=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;s.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:[^;{\\s\"']|\\s+(?!\\s)|"+e.source+")*?(?:;|(?=\\s*\\{))"),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+e.source+"|(?:[^\\\\\r\n()\"']|\\\\[^])*)\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+e.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+e.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:e,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var t=s.languages.markup;t&&(t.tag.addInlined("style","css"),t.tag.addAttribute("style","css"))}(Prism);
6
- Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/};
7
- Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp("(^|[^\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\dA-Fa-f]+(?:_[\\dA-Fa-f]+)*n?|\\d+(?:_\\d+)*n|(?:\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\.\\d+(?:_\\d+)*)(?:[Ee][+-]?\\d+(?:_\\d+)*)?)(?![\\w$])"),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp("((?:^|[^$\\w\\xA0-\\uFFFF.\"'\\])\\s]|\\b(?:return|yield))\\s*)/(?:(?:\\[(?:[^\\]\\\\\r\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}|(?:\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\s|/\\*(?:[^*]|\\*(?!/))*\\*/)*(?:$|[\r\n,.;:})\\]]|//))"),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),Prism.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute("on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)","javascript")),Prism.languages.js=Prism.languages.javascript;
8
- Prism.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},Prism.languages.webmanifest=Prism.languages.json;
9
- !function(n){var e=/("|')(?:\\(?:\r\n?|\n|.)|(?!\1)[^\\\r\n])*\1/;n.languages.json5=n.languages.extend("json",{property:[{pattern:RegExp(e.source+"(?=\\s*:)"),greedy:!0},{pattern:/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/,alias:"unquoted"}],string:{pattern:e,greedy:!0},number:/[+-]?\b(?:NaN|Infinity|0x[a-fA-F\d]+)\b|[+-]?(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[eE][+-]?\d+\b)?/})}(Prism);
10
- Prism.languages.log={string:{pattern:/"(?:[^"\\\r\n]|\\.)*"|'(?![st] | \w)(?:[^'\\\r\n]|\\.)*'/,greedy:!0},exception:{pattern:/(^|[^\w.])[a-z][\w.]*(?:Error|Exception):.*(?:(?:\r\n?|\n)[ \t]*(?:at[ \t].+|\.{3}.*|Caused by:.*))+(?:(?:\r\n?|\n)[ \t]*\.\.\. .*)?/,lookbehind:!0,greedy:!0,alias:["javastacktrace","language-javastacktrace"],inside:Prism.languages.javastacktrace||{keyword:/\bat\b/,function:/[a-z_][\w$]*(?=\()/,punctuation:/[.:()]/}},level:[{pattern:/\b(?:ALERT|CRIT|CRITICAL|EMERG|EMERGENCY|ERR|ERROR|FAILURE|FATAL|SEVERE)\b/,alias:["error","important"]},{pattern:/\b(?:WARN|WARNING|WRN)\b/,alias:["warning","important"]},{pattern:/\b(?:DISPLAY|INF|INFO|NOTICE|STATUS)\b/,alias:["info","keyword"]},{pattern:/\b(?:DBG|DEBUG|FINE)\b/,alias:["debug","keyword"]},{pattern:/\b(?:FINER|FINEST|TRACE|TRC|VERBOSE|VRB)\b/,alias:["trace","comment"]}],property:{pattern:/((?:^|[\]|])[ \t]*)[a-z_](?:[\w-]|\b\/\b)*(?:[. ]\(?\w(?:[\w-]|\b\/\b)*\)?)*:(?=\s)/im,lookbehind:!0},separator:{pattern:/(^|[^-+])-{3,}|={3,}|\*{3,}|- - /m,lookbehind:!0,alias:"comment"},url:/\b(?:file|ftp|https?):\/\/[^\s|,;'"]*[^\s|,;'">.]/,email:{pattern:/(^|\s)[-\w+.]+@[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)+(?=\s)/,lookbehind:!0,alias:"url"},"ip-address":{pattern:/\b(?:\d{1,3}(?:\.\d{1,3}){3})\b/,alias:"constant"},"mac-address":{pattern:/\b[a-f0-9]{2}(?::[a-f0-9]{2}){5}\b/i,alias:"constant"},domain:{pattern:/(^|\s)[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)*\.[a-z][a-z0-9-]+(?=\s)/,lookbehind:!0,alias:"constant"},uuid:{pattern:/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i,alias:"constant"},hash:{pattern:/\b(?:[a-f0-9]{32}){1,2}\b/i,alias:"constant"},"file-path":{pattern:/\b[a-z]:[\\/][^\s|,;:(){}\[\]"']+|(^|[\s:\[\](>|])\.{0,2}\/\w[^\s|,;:(){}\[\]"']*/i,lookbehind:!0,greedy:!0,alias:"string"},date:{pattern:RegExp("\\b\\d{4}[-/]\\d{2}[-/]\\d{2}(?:T(?=\\d{1,2}:)|(?=\\s\\d{1,2}:))|\\b\\d{1,4}[-/ ](?:\\d{1,2}|Apr|Aug|Dec|Feb|Jan|Jul|Jun|Mar|May|Nov|Oct|Sep)[-/ ]\\d{2,4}T?\\b|\\b(?:(?:Fri|Mon|Sat|Sun|Thu|Tue|Wed)(?:\\s{1,2}(?:Apr|Aug|Dec|Feb|Jan|Jul|Jun|Mar|May|Nov|Oct|Sep))?|Apr|Aug|Dec|Feb|Jan|Jul|Jun|Mar|May|Nov|Oct|Sep)\\s{1,2}\\d{1,2}\\b","i"),alias:"number"},time:{pattern:/\b\d{1,2}:\d{1,2}:\d{1,2}(?:[.,:]\d+)?(?:\s?[+-]\d{2}:?\d{2}|Z)?\b/,alias:"number"},boolean:/\b(?:false|null|true)\b/i,number:{pattern:/(^|[^.\w])(?:0x[a-f0-9]+|0o[0-7]+|0b[01]+|v?\d[\da-f]*(?:\.\d+)*(?:e[+-]?\d+)?[a-z]{0,3}\b)\b(?!\.\w)/i,lookbehind:!0},operator:/[;:?<=>~/@!$%&+\-|^(){}*#]/,punctuation:/[\[\].,]/};
11
- !function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){var e=[],t={},n=function(){};Prism.plugins.toolbar={};var a=Prism.plugins.toolbar.registerButton=function(n,a){var r;r="function"==typeof a?a:function(e){var t;return"function"==typeof a.onClick?((t=document.createElement("button")).type="button",t.addEventListener("click",(function(){a.onClick.call(this,e)}))):"string"==typeof a.url?(t=document.createElement("a")).href=a.url:t=document.createElement("span"),a.className&&t.classList.add(a.className),t.textContent=a.text,t},n in t?console.warn('There is a button with the key "'+n+'" registered already.'):e.push(t[n]=r)},r=Prism.plugins.toolbar.hook=function(a){var r=a.element.parentNode;if(r&&/pre/i.test(r.nodeName)&&!r.parentNode.classList.contains("code-toolbar")){var o=document.createElement("div");o.classList.add("code-toolbar"),r.parentNode.insertBefore(o,r),o.appendChild(r);var i=document.createElement("div");i.classList.add("toolbar");var l=e,d=function(e){for(;e;){var t=e.getAttribute("data-toolbar-order");if(null!=t)return(t=t.trim()).length?t.split(/\s*,\s*/g):[];e=e.parentElement}}(a.element);d&&(l=d.map((function(e){return t[e]||n}))),l.forEach((function(e){var t=e(a);if(t){var n=document.createElement("div");n.classList.add("toolbar-item"),n.appendChild(t),i.appendChild(n)}})),o.appendChild(i)}};a("label",(function(e){var t=e.element.parentNode;if(t&&/pre/i.test(t.nodeName)&&t.hasAttribute("data-label")){var n,a,r=t.getAttribute("data-label");try{a=document.querySelector("template#"+r)}catch(e){}return a?n=a.content:(t.hasAttribute("data-url")?(n=document.createElement("a")).href=t.getAttribute("data-url"):n=document.createElement("span"),n.textContent=r),n}})),Prism.hooks.add("complete",r)}}();
12
- !function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document)if(Prism.plugins.toolbar){var e={none:"Plain text",plain:"Plain text",plaintext:"Plain text",text:"Plain text",txt:"Plain text",html:"HTML",xml:"XML",svg:"SVG",mathml:"MathML",ssml:"SSML",rss:"RSS",css:"CSS",clike:"C-like",js:"JavaScript",abap:"ABAP",abnf:"ABNF",al:"AL",antlr4:"ANTLR4",g4:"ANTLR4",apacheconf:"Apache Configuration",apl:"APL",aql:"AQL",ino:"Arduino",arff:"ARFF",armasm:"ARM Assembly","arm-asm":"ARM Assembly",art:"Arturo",asciidoc:"AsciiDoc",adoc:"AsciiDoc",aspnet:"ASP.NET (C#)",asm6502:"6502 Assembly",asmatmel:"Atmel AVR Assembly",autohotkey:"AutoHotkey",autoit:"AutoIt",avisynth:"AviSynth",avs:"AviSynth","avro-idl":"Avro IDL",avdl:"Avro IDL",awk:"AWK",gawk:"GAWK",sh:"Shell",basic:"BASIC",bbcode:"BBcode",bbj:"BBj",bnf:"BNF",rbnf:"RBNF",bqn:"BQN",bsl:"BSL (1C:Enterprise)",oscript:"OneScript",csharp:"C#",cs:"C#",dotnet:"C#",cpp:"C++",cfscript:"CFScript",cfc:"CFScript",cil:"CIL",cilkc:"Cilk/C","cilk-c":"Cilk/C",cilkcpp:"Cilk/C++","cilk-cpp":"Cilk/C++",cilk:"Cilk/C++",cmake:"CMake",cobol:"COBOL",coffee:"CoffeeScript",conc:"Concurnas",csp:"Content-Security-Policy","css-extras":"CSS Extras",csv:"CSV",cue:"CUE",dataweave:"DataWeave",dax:"DAX",django:"Django/Jinja2",jinja2:"Django/Jinja2","dns-zone-file":"DNS zone file","dns-zone":"DNS zone file",dockerfile:"Docker",dot:"DOT (Graphviz)",gv:"DOT (Graphviz)",ebnf:"EBNF",editorconfig:"EditorConfig",ejs:"EJS",etlua:"Embedded Lua templating",erb:"ERB","excel-formula":"Excel Formula",xlsx:"Excel Formula",xls:"Excel Formula",fsharp:"F#","firestore-security-rules":"Firestore security rules",ftl:"FreeMarker Template Language",gml:"GameMaker Language",gamemakerlanguage:"GameMaker Language",gap:"GAP (CAS)",gcode:"G-code",gdscript:"GDScript",gedcom:"GEDCOM",gettext:"gettext",po:"gettext",glsl:"GLSL",gn:"GN",gni:"GN","linker-script":"GNU Linker Script",ld:"GNU Linker Script","go-module":"Go module","go-mod":"Go module",graphql:"GraphQL",hbs:"Handlebars",hs:"Haskell",hcl:"HCL",hlsl:"HLSL",http:"HTTP",hpkp:"HTTP Public-Key-Pins",hsts:"HTTP Strict-Transport-Security",ichigojam:"IchigoJam","icu-message-format":"ICU Message Format",idr:"Idris",ignore:".ignore",gitignore:".gitignore",hgignore:".hgignore",npmignore:".npmignore",inform7:"Inform 7",javadoc:"JavaDoc",javadoclike:"JavaDoc-like",javastacktrace:"Java stack trace",jq:"JQ",jsdoc:"JSDoc","js-extras":"JS Extras",json:"JSON",webmanifest:"Web App Manifest",json5:"JSON5",jsonp:"JSONP",jsstacktrace:"JS stack trace","js-templates":"JS Templates",keepalived:"Keepalived Configure",kts:"Kotlin Script",kt:"Kotlin",kumir:"KuMir (КуМир)",kum:"KuMir (КуМир)",latex:"LaTeX",tex:"TeX",context:"ConTeXt",lilypond:"LilyPond",ly:"LilyPond",emacs:"Lisp",elisp:"Lisp","emacs-lisp":"Lisp",llvm:"LLVM IR",log:"Log file",lolcode:"LOLCODE",magma:"Magma (CAS)",md:"Markdown","markup-templating":"Markup templating",matlab:"MATLAB",maxscript:"MAXScript",mel:"MEL",metafont:"METAFONT",mongodb:"MongoDB",moon:"MoonScript",n1ql:"N1QL",n4js:"N4JS",n4jsd:"N4JS","nand2tetris-hdl":"Nand To Tetris HDL",naniscript:"Naninovel Script",nani:"Naninovel Script",nasm:"NASM",neon:"NEON",nginx:"nginx",nsis:"NSIS",objectivec:"Objective-C",objc:"Objective-C",ocaml:"OCaml",opencl:"OpenCL",openqasm:"OpenQasm",qasm:"OpenQasm",parigp:"PARI/GP",objectpascal:"Object Pascal",psl:"PATROL Scripting Language",pcaxis:"PC-Axis",px:"PC-Axis",peoplecode:"PeopleCode",pcode:"PeopleCode",php:"PHP",phpdoc:"PHPDoc","php-extras":"PHP Extras","plant-uml":"PlantUML",plantuml:"PlantUML",plsql:"PL/SQL",powerquery:"PowerQuery",pq:"PowerQuery",mscript:"PowerQuery",powershell:"PowerShell",promql:"PromQL",properties:".properties",protobuf:"Protocol Buffers",purebasic:"PureBasic",pbfasm:"PureBasic",purs:"PureScript",py:"Python",qsharp:"Q#",qs:"Q#",q:"Q (kdb+ database)",qml:"QML",rkt:"Racket",cshtml:"Razor C#",razor:"Razor C#",jsx:"React JSX",tsx:"React TSX",renpy:"Ren'py",rpy:"Ren'py",res:"ReScript",rest:"reST (reStructuredText)",robotframework:"Robot Framework",robot:"Robot Framework",rb:"Ruby",sas:"SAS",sass:"Sass (Sass)",scss:"Sass (SCSS)","shell-session":"Shell session","sh-session":"Shell session",shellsession:"Shell session",sml:"SML",smlnj:"SML/NJ",solidity:"Solidity (Ethereum)",sol:"Solidity (Ethereum)","solution-file":"Solution file",sln:"Solution file",soy:"Soy (Closure Template)",sparql:"SPARQL",rq:"SPARQL","splunk-spl":"Splunk SPL",sqf:"SQF: Status Quo Function (Arma 3)",sql:"SQL",stata:"Stata Ado",iecst:"Structured Text (IEC 61131-3)",supercollider:"SuperCollider",sclang:"SuperCollider",systemd:"Systemd configuration file","t4-templating":"T4 templating","t4-cs":"T4 Text Templates (C#)",t4:"T4 Text Templates (C#)","t4-vb":"T4 Text Templates (VB)",tap:"TAP",tt2:"Template Toolkit 2",toml:"TOML",trickle:"trickle",troy:"troy",trig:"TriG",ts:"TypeScript",tsconfig:"TSConfig",uscript:"UnrealScript",uc:"UnrealScript",uorazor:"UO Razor Script",uri:"URI",url:"URL",vbnet:"VB.Net",vhdl:"VHDL",vim:"vim","visual-basic":"Visual Basic",vba:"VBA",vb:"Visual Basic",wasm:"WebAssembly","web-idl":"Web IDL",webidl:"Web IDL",wgsl:"WGSL",wiki:"Wiki markup",wolfram:"Wolfram language",nb:"Mathematica Notebook",wl:"Wolfram language",xeoracube:"XeoraCube","xml-doc":"XML doc (.net)",xojo:"Xojo (REALbasic)",xquery:"XQuery",yaml:"YAML",yml:"YAML",yang:"YANG"};Prism.plugins.toolbar.registerButton("show-language",(function(a){var t=a.element.parentNode;if(t&&/pre/i.test(t.nodeName)){var o,i=t.getAttribute("data-language")||e[a.language]||((o=a.language)?(o.substring(0,1).toUpperCase()+o.substring(1)).replace(/s(?=cript)/,"S"):o);if(i){var s=document.createElement("span");return s.textContent=i,s}}}))}else console.warn("Show Languages plugin loaded before Toolbar plugin.")}();
@@ -1,114 +0,0 @@
1
- var cleared = false;
2
-
3
- function size_dict(d){c=0; for (i in d) ++c; return c}
4
-
5
- function init_messages() {
6
- const socket = io("/sendmsg");
7
- socket.on('connect', function () {
8
- console.log("Connected to socketio");
9
- });
10
- socket.on('connected', function(msg) {
11
- console.log("Connected!");
12
- console.log(msg);
13
- });
14
-
15
- socket.on("sent", function(msg) {
16
- if (cleared == false) {
17
- var msgsdiv = $("#msgsDiv");
18
- msgsdiv.html('')
19
- cleared = true
20
- }
21
- add_msg(msg);
22
- });
23
-
24
- socket.on("ack", function(msg) {
25
- update_msg(msg);
26
- });
27
- socket.on("reply", function(msg) {
28
- update_msg(msg);
29
- });
30
-
31
- }
32
-
33
- function add_msg(msg) {
34
- var msgsdiv = $("#sendMsgsDiv");
35
-
36
- ts_str = msg["ts"].toString();
37
- ts = ts_str.split(".")[0]*1000;
38
- var d = new Date(ts).toLocaleDateString("en-US")
39
- var t = new Date(ts).toLocaleTimeString("en-US")
40
-
41
- from = msg['from']
42
- title_id = 'title_tx'
43
- var from_to = d + " " + t + "&nbsp;&nbsp;&nbsp;&nbsp;" + from + " > "
44
-
45
- if (msg.hasOwnProperty('to')) {
46
- from_to = from_to + msg['to']
47
- }
48
- from_to = from_to + "&nbsp;&nbsp;-&nbsp;&nbsp;" + msg['message']
49
-
50
- id = ts_str.split('.')[0]
51
- pretty_id = "pretty_" + id
52
- loader_id = "loader_" + id
53
- ack_id = "ack_" + id
54
- reply_id = "reply_" + id
55
- span_id = "span_" + id
56
- json_pretty = Prism.highlight(JSON.stringify(msg, null, '\t'), Prism.languages.json, 'json');
57
- msg_html = '<div class="ui title" id="' + title_id + '"><i class="dropdown icon"></i>';
58
- msg_html += '<div class="ui active inline loader" id="' + loader_id +'" data-content="Waiting for Ack"></div>&nbsp;';
59
- msg_html += '<i class="thumbs down outline icon" id="' + ack_id + '" data-content="Waiting for ACK"></i>&nbsp;';
60
- msg_html += '<i class="thumbs down outline icon" id="' + reply_id + '" data-content="Waiting for Reply"></i>&nbsp;';
61
- msg_html += '<span id="' + span_id + '">' + from_to +'</span></div>';
62
- msg_html += '<div class="content"><p class="transition hidden"><pre id="' + pretty_id + '" class="language-json">' + json_pretty + '</p></p></div>'
63
- msgsdiv.prepend(msg_html);
64
- $('.ui.accordion').accordion('refresh');
65
- }
66
-
67
- function update_msg(msg) {
68
- var msgsdiv = $("#sendMsgsDiv");
69
- // We have an existing entry
70
- ts_str = msg["ts"].toString();
71
- id = ts_str.split('.')[0]
72
- pretty_id = "pretty_" + id
73
- loader_id = "loader_" + id
74
- reply_id = "reply_" + id
75
- ack_id = "ack_" + id
76
- span_id = "span_" + id
77
-
78
-
79
-
80
- if (msg['ack'] == true) {
81
- var loader_div = $('#' + loader_id);
82
- var ack_div = $('#' + ack_id);
83
- loader_div.removeClass('ui active inline loader');
84
- loader_div.addClass('ui disabled loader');
85
- ack_div.removeClass('thumbs up outline icon');
86
- ack_div.addClass('thumbs up outline icon');
87
- }
88
-
89
- if (msg['reply'] !== null) {
90
- var reply_div = $('#' + reply_id);
91
- reply_div.removeClass("thumbs down outline icon");
92
- reply_div.addClass('reply icon');
93
- reply_div.attr('data-content', 'Got Reply');
94
-
95
- var d = new Date(ts).toLocaleDateString("en-US")
96
- var t = new Date(ts).toLocaleTimeString("en-US")
97
- var from_to = d + " " + t + "&nbsp;&nbsp;&nbsp;&nbsp;" + from + " > "
98
-
99
- if (msg.hasOwnProperty('to')) {
100
- from_to = from_to + msg['to']
101
- }
102
- from_to = from_to + "&nbsp;&nbsp;-&nbsp;&nbsp;" + msg['message']
103
- from_to += "&nbsp;&nbsp; ===> " + msg["reply"]["message_text"]
104
-
105
- var span_div = $('#' + span_id);
106
- span_div.html(from_to);
107
- }
108
-
109
- var pretty_pre = $("#" + pretty_id);
110
- pretty_pre.html('');
111
- json_pretty = Prism.highlight(JSON.stringify(msg, null, '\t'), Prism.languages.json, 'json');
112
- pretty_pre.html(json_pretty);
113
- $('.ui.accordion').accordion('refresh');
114
- }
@@ -1,28 +0,0 @@
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
- }
@@ -1,196 +0,0 @@
1
- <html>
2
- <head>
3
- <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
4
- <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
5
- <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
6
- <script src="https://cdn.socket.io/4.7.1/socket.io.min.js" integrity="sha512-+NaO7d6gQ1YPxvc/qHIqZEchjGm207SszoNeMgppoqD/67fEqmc1edS8zrbxPD+4RQI3gDgT/83ihpFW61TG/Q==" crossorigin="anonymous"></script>
7
-
8
- <script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.bundle.js"></script>
9
- <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
10
-
11
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css">
12
- <script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.js"></script>
13
-
14
- <link rel="stylesheet" href="/static/css/index.css">
15
- <link rel="stylesheet" href="/static/css/tabs.css">
16
- <link rel="stylesheet" href="/static/css/prism.css">
17
- <script src="/static/js/prism.js"></script>
18
- <script src="/static/js/main.js"></script>
19
- <script src="/static/js/echarts.js"></script>
20
- <script src="/static/js/tabs.js"></script>
21
- <script src="/static/js/send-message.js"></script>
22
- <script src="/static/js/logs.js"></script>
23
-
24
-
25
- <script type="text/javascript">
26
- var initial_stats = {{ initial_stats|tojson|safe }};
27
-
28
- var memory_chart = null
29
- var message_chart = null
30
- var color = Chart.helpers.color;
31
-
32
- $(document).ready(function() {
33
- start_update();
34
- start_charts();
35
- init_messages();
36
- init_logs();
37
-
38
- $("#toggleStats").click(function() {
39
- $("#jsonstats").fadeToggle(1000);
40
- });
41
-
42
- // Pretty print the config json so it's readable
43
- var cfg_data = $("#configjson").text();
44
- var cfg_json = JSON.parse(cfg_data);
45
- var cfg_pretty = JSON.stringify(cfg_json, null, '\t');
46
- const html_pretty = Prism.highlight( cfg_pretty, Prism.languages.json, 'json');
47
- $("#configjson").html(html_pretty);
48
- $("#jsonstats").fadeToggle(1000);
49
-
50
- //var log_text_pretty = $('#logtext').text();
51
- //const log_pretty = Prism.highlight( log_text_pretty, Prism.languages.log, 'log');
52
- //$('#logtext').html(log_pretty);
53
-
54
- $('.ui.accordion').accordion({exclusive: false});
55
- $('.menu .item').tab('change tab', 'charts-tab');
56
- });
57
- </script>
58
- </head>
59
-
60
- <body>
61
- <div class='ui text container'>
62
- <h1 class='ui dividing header'>APRSD {{ version }}</h1>
63
- </div>
64
-
65
- <div class='ui grid text container'>
66
- <div class='left floated ten wide column'>
67
- <span style='color: green'>{{ callsign }}</span>
68
- connected to
69
- <span style='color: blue' id='aprs_connection'>{{ aprs_connection|safe }}</span>
70
- </div>
71
-
72
- <div class='right floated four wide column'>
73
- <span id='uptime'>NONE</span>
74
- </div>
75
- </div>
76
-
77
- <!-- Tab links -->
78
- <div class="ui top attached tabular menu">
79
- <div class="active item" data-tab="charts-tab">Charts</div>
80
- <div class="item" data-tab="msgs-tab">Messages</div>
81
- <div class="item" data-tab="seen-tab">Seen List</div>
82
- <div class="item" data-tab="watch-tab">Watch List</div>
83
- <div class="item" data-tab="plugin-tab">Plugins</div>
84
- <div class="item" data-tab="threads-tab">Threads</div>
85
- <div class="item" data-tab="config-tab">Config</div>
86
- <div class="item" data-tab="log-tab">LogFile</div>
87
- <!-- <div class="item" data-tab="oslo-tab">OSLO CONFIG</div> //-->
88
- <div class="item" data-tab="raw-tab">Raw JSON</div>
89
- </div>
90
-
91
- <!-- Tab content -->
92
- <div class="ui bottom attached active tab segment" data-tab="charts-tab">
93
- <h3 class="ui dividing header">Charts</h3>
94
- <div class="ui equal width relaxed grid">
95
- <div class="row">
96
- <div class="column">
97
- <div class="ui segment" style="height: 300px" id="packetsChart"></div>
98
- </div>
99
- </div>
100
- <div class="row">
101
- <div class="column">
102
- <div class="ui segment" style="height: 300px" id="messagesChart"></div>
103
- </div>
104
- <div class="column">
105
- <div class="ui segment" style="height: 300px" id="acksChart"></div>
106
- </div>
107
- </div>
108
- <div class="row">
109
- <div class="column">
110
- <div class="ui segment" style="height: 300px" id="packetTypesChart"></div>
111
- </div>
112
- </div>
113
- <div class="row">
114
- <div class="column">
115
- <div class="ui segment" style="height: 300px" id="threadChart"></div>
116
- </div>
117
- </div>
118
- <div class="row">
119
- <div class="column">
120
- <div class="ui segment" style="height: 300px" id="memChart"></div>
121
- </div>
122
- </div>
123
- <!-- <div class="row">
124
- <div id="stats" class="two column">
125
- <button class="ui button" id="toggleStats">Toggle raw json</button>
126
- <pre id="jsonstats" class="language-json">{{ stats }}</pre>
127
- </div> //-->
128
- </div>
129
- </div>
130
-
131
- </div>
132
-
133
- <div class="ui bottom attached tab segment" data-tab="msgs-tab">
134
- <h3 class="ui dividing header">Messages (<span id="packets_count">0</span>)</h3>
135
- <div class="ui styled fluid accordion" id="accordion">
136
- <div id="packetsDiv" class="ui mini text">Loading</div>
137
- </div>
138
- </div>
139
-
140
- <div class="ui bottom attached tab segment" data-tab="seen-tab">
141
- <h3 class="ui dividing header">
142
- Callsign Seen List (<span id="seen_count">{{ seen_count }}</span>)
143
- </h3>
144
- <div id="seenDiv" class="ui mini text">Loading</div>
145
- </div>
146
-
147
- <div class="ui bottom attached tab segment" data-tab="watch-tab">
148
- <h3 class="ui dividing header">
149
- Callsign Watch List (<span id="watch_count">{{ watch_count }}</span>)
150
- &nbsp;&nbsp;&nbsp;
151
- Notification age - <span id="watch_age">{{ watch_age }}</span>
152
- </h3>
153
- <div id="watchDiv" class="ui mini text">Loading</div>
154
- </div>
155
-
156
- <div class="ui bottom attached tab segment" data-tab="plugin-tab">
157
- <h3 class="ui dividing header">
158
- Plugins Loaded (<span id="plugin_count">{{ plugin_count }}</span>)
159
- </h3>
160
- <div id="pluginDiv" class="ui mini text">Loading</div>
161
- </div>
162
-
163
- <div class="ui bottom attached tab segment" data-tab="threads-tab">
164
- <h3 class="ui dividing header">
165
- Threads Loaded (<span id="thread_count">{{ thread_count }}</span>)
166
- </h3>
167
- <div id="threadsDiv" class="ui mini text">Loading</div>
168
- </div>
169
-
170
- <div class="ui bottom attached tab segment" data-tab="config-tab">
171
- <h3 class="ui dividing header">Config</h3>
172
- <pre id="configjson" class="language-json">{{ config_json|safe }}</pre>
173
- </div>
174
-
175
- <div class="ui bottom attached tab segment" data-tab="log-tab">
176
- <h3 class="ui dividing header">LOGFILE</h3>
177
- <pre id="logContainer" style="height: 600px;overflow-y:auto;overflow-x:auto;"><code id="logtext" class="language-log" ></code></pre>
178
- </div>
179
-
180
- <!--
181
- <div class="ui bottom attached tab segment" data-tab="oslo-tab">
182
- <h3 class="ui dividing header">OSLO</h3>
183
- <pre id="osloContainer" style="height:600px;overflow-y:auto;" class="language-json">{{ oslo_out|safe }}</pre>
184
- </div> //-->
185
-
186
- <div class="ui bottom attached tab segment" data-tab="raw-tab">
187
- <h3 class="ui dividing header">Raw JSON</h3>
188
- <pre id="jsonstats" class="language-yaml" style="height:600px;overflow-y:auto;">{{ initial_stats|safe }}</pre>
189
- </div>
190
-
191
- <div class="ui text container">
192
- <a href="https://badge.fury.io/py/aprsd"><img src="https://badge.fury.io/py/aprsd.svg" alt="PyPI version" height="18"></a>
193
- <a href="https://github.com/craigerl/aprsd"><img src="https://img.shields.io/badge/Made%20with-Python-1f425f.svg" height="18"></a>
194
- </div>
195
- </body>
196
- </html>