aprsd 3.3.3__py2.py3-none-any.whl → 3.4.0__py2.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.
- aprsd/client.py +133 -20
- aprsd/clients/aprsis.py +6 -3
- aprsd/clients/fake.py +1 -1
- aprsd/clients/kiss.py +1 -1
- aprsd/cmds/completion.py +13 -27
- aprsd/cmds/fetch_stats.py +53 -57
- aprsd/cmds/healthcheck.py +32 -30
- aprsd/cmds/list_plugins.py +2 -2
- aprsd/cmds/listen.py +33 -17
- aprsd/cmds/send_message.py +2 -2
- aprsd/cmds/server.py +26 -9
- aprsd/cmds/webchat.py +34 -29
- aprsd/conf/common.py +46 -31
- aprsd/log/log.py +28 -6
- aprsd/main.py +20 -18
- aprsd/packets/__init__.py +3 -2
- aprsd/packets/collector.py +56 -0
- aprsd/packets/core.py +456 -321
- aprsd/packets/log.py +143 -0
- aprsd/packets/packet_list.py +83 -66
- aprsd/packets/seen_list.py +30 -19
- aprsd/packets/tracker.py +60 -62
- aprsd/packets/watch_list.py +64 -38
- aprsd/plugin.py +41 -16
- aprsd/plugins/email.py +35 -7
- aprsd/plugins/time.py +3 -2
- aprsd/plugins/version.py +4 -5
- aprsd/plugins/weather.py +0 -1
- aprsd/stats/__init__.py +20 -0
- aprsd/stats/app.py +46 -0
- aprsd/stats/collector.py +38 -0
- aprsd/threads/__init__.py +3 -2
- aprsd/threads/aprsd.py +67 -36
- aprsd/threads/keep_alive.py +55 -49
- aprsd/threads/log_monitor.py +46 -0
- aprsd/threads/rx.py +43 -24
- aprsd/threads/stats.py +44 -0
- aprsd/threads/tx.py +36 -17
- aprsd/utils/__init__.py +12 -0
- aprsd/utils/counter.py +6 -3
- aprsd/utils/json.py +20 -0
- aprsd/utils/objectstore.py +22 -17
- aprsd/web/admin/static/css/prism.css +4 -189
- aprsd/web/admin/static/js/charts.js +9 -7
- aprsd/web/admin/static/js/echarts.js +71 -9
- aprsd/web/admin/static/js/main.js +47 -6
- aprsd/web/admin/static/js/prism.js +11 -2246
- aprsd/web/admin/templates/index.html +18 -7
- aprsd/web/chat/static/js/gps.js +3 -1
- aprsd/web/chat/static/js/main.js +4 -3
- aprsd/web/chat/static/js/send-message.js +5 -2
- aprsd/web/chat/templates/index.html +1 -0
- aprsd/wsgi.py +62 -127
- {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/METADATA +14 -16
- {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/RECORD +60 -65
- {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/WHEEL +1 -1
- aprsd-3.4.0.dist-info/pbr.json +1 -0
- aprsd/plugins/query.py +0 -81
- aprsd/rpc/__init__.py +0 -14
- aprsd/rpc/client.py +0 -165
- aprsd/rpc/server.py +0 -99
- aprsd/stats.py +0 -266
- aprsd/threads/store.py +0 -30
- aprsd/utils/converters.py +0 -15
- aprsd/web/admin/static/json-viewer/jquery.json-viewer.css +0 -57
- aprsd/web/admin/static/json-viewer/jquery.json-viewer.js +0 -158
- aprsd/web/chat/static/json-viewer/jquery.json-viewer.css +0 -57
- aprsd/web/chat/static/json-viewer/jquery.json-viewer.js +0 -158
- aprsd-3.3.3.dist-info/pbr.json +0 -1
- {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/LICENSE +0 -0
- {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/entry_points.txt +0 -0
- {aprsd-3.3.3.dist-info → aprsd-3.4.0.dist-info}/top_level.txt +0 -0
aprsd/stats.py
DELETED
@@ -1,266 +0,0 @@
|
|
1
|
-
import datetime
|
2
|
-
import logging
|
3
|
-
import threading
|
4
|
-
|
5
|
-
from oslo_config import cfg
|
6
|
-
import wrapt
|
7
|
-
|
8
|
-
import aprsd
|
9
|
-
from aprsd import packets, plugin, utils
|
10
|
-
|
11
|
-
|
12
|
-
CONF = cfg.CONF
|
13
|
-
LOG = logging.getLogger("APRSD")
|
14
|
-
|
15
|
-
|
16
|
-
class APRSDStats:
|
17
|
-
|
18
|
-
_instance = None
|
19
|
-
lock = threading.Lock()
|
20
|
-
|
21
|
-
start_time = None
|
22
|
-
_aprsis_server = None
|
23
|
-
_aprsis_keepalive = None
|
24
|
-
|
25
|
-
_email_thread_last_time = None
|
26
|
-
_email_tx = 0
|
27
|
-
_email_rx = 0
|
28
|
-
|
29
|
-
_mem_current = 0
|
30
|
-
_mem_peak = 0
|
31
|
-
|
32
|
-
_thread_info = {}
|
33
|
-
|
34
|
-
_pkt_cnt = {
|
35
|
-
"Packet": {
|
36
|
-
"tx": 0,
|
37
|
-
"rx": 0,
|
38
|
-
},
|
39
|
-
"AckPacket": {
|
40
|
-
"tx": 0,
|
41
|
-
"rx": 0,
|
42
|
-
},
|
43
|
-
"GPSPacket": {
|
44
|
-
"tx": 0,
|
45
|
-
"rx": 0,
|
46
|
-
},
|
47
|
-
"StatusPacket": {
|
48
|
-
"tx": 0,
|
49
|
-
"rx": 0,
|
50
|
-
},
|
51
|
-
"MicEPacket": {
|
52
|
-
"tx": 0,
|
53
|
-
"rx": 0,
|
54
|
-
},
|
55
|
-
"MessagePacket": {
|
56
|
-
"tx": 0,
|
57
|
-
"rx": 0,
|
58
|
-
},
|
59
|
-
"WeatherPacket": {
|
60
|
-
"tx": 0,
|
61
|
-
"rx": 0,
|
62
|
-
},
|
63
|
-
"ObjectPacket": {
|
64
|
-
"tx": 0,
|
65
|
-
"rx": 0,
|
66
|
-
},
|
67
|
-
}
|
68
|
-
|
69
|
-
def __new__(cls, *args, **kwargs):
|
70
|
-
if cls._instance is None:
|
71
|
-
cls._instance = super().__new__(cls)
|
72
|
-
# any init here
|
73
|
-
cls._instance.start_time = datetime.datetime.now()
|
74
|
-
cls._instance._aprsis_keepalive = datetime.datetime.now()
|
75
|
-
return cls._instance
|
76
|
-
|
77
|
-
@wrapt.synchronized(lock)
|
78
|
-
@property
|
79
|
-
def uptime(self):
|
80
|
-
return datetime.datetime.now() - self.start_time
|
81
|
-
|
82
|
-
@wrapt.synchronized(lock)
|
83
|
-
@property
|
84
|
-
def memory(self):
|
85
|
-
return self._mem_current
|
86
|
-
|
87
|
-
@wrapt.synchronized(lock)
|
88
|
-
def set_memory(self, memory):
|
89
|
-
self._mem_current = memory
|
90
|
-
|
91
|
-
@wrapt.synchronized(lock)
|
92
|
-
@property
|
93
|
-
def memory_peak(self):
|
94
|
-
return self._mem_peak
|
95
|
-
|
96
|
-
@wrapt.synchronized(lock)
|
97
|
-
def set_memory_peak(self, memory):
|
98
|
-
self._mem_peak = memory
|
99
|
-
|
100
|
-
@wrapt.synchronized(lock)
|
101
|
-
def set_thread_info(self, thread_info):
|
102
|
-
self._thread_info = thread_info
|
103
|
-
|
104
|
-
@wrapt.synchronized(lock)
|
105
|
-
@property
|
106
|
-
def thread_info(self):
|
107
|
-
return self._thread_info
|
108
|
-
|
109
|
-
@wrapt.synchronized(lock)
|
110
|
-
@property
|
111
|
-
def aprsis_server(self):
|
112
|
-
return self._aprsis_server
|
113
|
-
|
114
|
-
@wrapt.synchronized(lock)
|
115
|
-
def set_aprsis_server(self, server):
|
116
|
-
self._aprsis_server = server
|
117
|
-
|
118
|
-
@wrapt.synchronized(lock)
|
119
|
-
@property
|
120
|
-
def aprsis_keepalive(self):
|
121
|
-
return self._aprsis_keepalive
|
122
|
-
|
123
|
-
@wrapt.synchronized(lock)
|
124
|
-
def set_aprsis_keepalive(self):
|
125
|
-
self._aprsis_keepalive = datetime.datetime.now()
|
126
|
-
|
127
|
-
def rx(self, packet):
|
128
|
-
pkt_type = packet.__class__.__name__
|
129
|
-
if pkt_type not in self._pkt_cnt:
|
130
|
-
self._pkt_cnt[pkt_type] = {
|
131
|
-
"tx": 0,
|
132
|
-
"rx": 0,
|
133
|
-
}
|
134
|
-
self._pkt_cnt[pkt_type]["rx"] += 1
|
135
|
-
|
136
|
-
def tx(self, packet):
|
137
|
-
pkt_type = packet.__class__.__name__
|
138
|
-
if pkt_type not in self._pkt_cnt:
|
139
|
-
self._pkt_cnt[pkt_type] = {
|
140
|
-
"tx": 0,
|
141
|
-
"rx": 0,
|
142
|
-
}
|
143
|
-
self._pkt_cnt[pkt_type]["tx"] += 1
|
144
|
-
|
145
|
-
@wrapt.synchronized(lock)
|
146
|
-
@property
|
147
|
-
def msgs_tracked(self):
|
148
|
-
return packets.PacketTrack().total_tracked
|
149
|
-
|
150
|
-
@wrapt.synchronized(lock)
|
151
|
-
@property
|
152
|
-
def email_tx(self):
|
153
|
-
return self._email_tx
|
154
|
-
|
155
|
-
@wrapt.synchronized(lock)
|
156
|
-
def email_tx_inc(self):
|
157
|
-
self._email_tx += 1
|
158
|
-
|
159
|
-
@wrapt.synchronized(lock)
|
160
|
-
@property
|
161
|
-
def email_rx(self):
|
162
|
-
return self._email_rx
|
163
|
-
|
164
|
-
@wrapt.synchronized(lock)
|
165
|
-
def email_rx_inc(self):
|
166
|
-
self._email_rx += 1
|
167
|
-
|
168
|
-
@wrapt.synchronized(lock)
|
169
|
-
@property
|
170
|
-
def email_thread_time(self):
|
171
|
-
return self._email_thread_last_time
|
172
|
-
|
173
|
-
@wrapt.synchronized(lock)
|
174
|
-
def email_thread_update(self):
|
175
|
-
self._email_thread_last_time = datetime.datetime.now()
|
176
|
-
|
177
|
-
@wrapt.synchronized(lock)
|
178
|
-
def stats(self):
|
179
|
-
now = datetime.datetime.now()
|
180
|
-
if self._email_thread_last_time:
|
181
|
-
last_update = str(now - self._email_thread_last_time)
|
182
|
-
else:
|
183
|
-
last_update = "never"
|
184
|
-
|
185
|
-
if self._aprsis_keepalive:
|
186
|
-
last_aprsis_keepalive = str(now - self._aprsis_keepalive)
|
187
|
-
else:
|
188
|
-
last_aprsis_keepalive = "never"
|
189
|
-
|
190
|
-
pm = plugin.PluginManager()
|
191
|
-
plugins = pm.get_plugins()
|
192
|
-
plugin_stats = {}
|
193
|
-
if plugins:
|
194
|
-
def full_name_with_qualname(obj):
|
195
|
-
return "{}.{}".format(
|
196
|
-
obj.__class__.__module__,
|
197
|
-
obj.__class__.__qualname__,
|
198
|
-
)
|
199
|
-
|
200
|
-
for p in plugins:
|
201
|
-
plugin_stats[full_name_with_qualname(p)] = {
|
202
|
-
"enabled": p.enabled,
|
203
|
-
"rx": p.rx_count,
|
204
|
-
"tx": p.tx_count,
|
205
|
-
"version": p.version,
|
206
|
-
}
|
207
|
-
|
208
|
-
wl = packets.WatchList()
|
209
|
-
sl = packets.SeenList()
|
210
|
-
pl = packets.PacketList()
|
211
|
-
|
212
|
-
stats = {
|
213
|
-
"aprsd": {
|
214
|
-
"version": aprsd.__version__,
|
215
|
-
"uptime": utils.strfdelta(self.uptime),
|
216
|
-
"callsign": CONF.callsign,
|
217
|
-
"memory_current": int(self.memory),
|
218
|
-
"memory_current_str": utils.human_size(self.memory),
|
219
|
-
"memory_peak": int(self.memory_peak),
|
220
|
-
"memory_peak_str": utils.human_size(self.memory_peak),
|
221
|
-
"threads": self._thread_info,
|
222
|
-
"watch_list": wl.get_all(),
|
223
|
-
"seen_list": sl.get_all(),
|
224
|
-
},
|
225
|
-
"aprs-is": {
|
226
|
-
"server": str(self.aprsis_server),
|
227
|
-
"callsign": CONF.aprs_network.login,
|
228
|
-
"last_update": last_aprsis_keepalive,
|
229
|
-
},
|
230
|
-
"packets": {
|
231
|
-
"total_tracked": int(pl.total_tx() + pl.total_rx()),
|
232
|
-
"total_sent": int(pl.total_tx()),
|
233
|
-
"total_received": int(pl.total_rx()),
|
234
|
-
"by_type": self._pkt_cnt,
|
235
|
-
},
|
236
|
-
"messages": {
|
237
|
-
"sent": self._pkt_cnt["MessagePacket"]["tx"],
|
238
|
-
"received": self._pkt_cnt["MessagePacket"]["tx"],
|
239
|
-
"ack_sent": self._pkt_cnt["AckPacket"]["tx"],
|
240
|
-
},
|
241
|
-
"email": {
|
242
|
-
"enabled": CONF.email_plugin.enabled,
|
243
|
-
"sent": int(self._email_tx),
|
244
|
-
"received": int(self._email_rx),
|
245
|
-
"thread_last_update": last_update,
|
246
|
-
},
|
247
|
-
"plugins": plugin_stats,
|
248
|
-
}
|
249
|
-
return stats
|
250
|
-
|
251
|
-
def __str__(self):
|
252
|
-
pl = packets.PacketList()
|
253
|
-
return (
|
254
|
-
"Uptime:{} Msgs TX:{} RX:{} "
|
255
|
-
"ACK: TX:{} RX:{} "
|
256
|
-
"Email TX:{} RX:{} LastLoop:{} ".format(
|
257
|
-
self.uptime,
|
258
|
-
pl.total_tx(),
|
259
|
-
pl.total_rx(),
|
260
|
-
self._pkt_cnt["AckPacket"]["tx"],
|
261
|
-
self._pkt_cnt["AckPacket"]["rx"],
|
262
|
-
self._email_tx,
|
263
|
-
self._email_rx,
|
264
|
-
self._email_thread_last_time,
|
265
|
-
)
|
266
|
-
)
|
aprsd/threads/store.py
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
import time
|
3
|
-
from typing import List
|
4
|
-
|
5
|
-
from oslo_config import cfg
|
6
|
-
|
7
|
-
from aprsd.threads import APRSDThread
|
8
|
-
from aprsd.utils import objectstore
|
9
|
-
|
10
|
-
|
11
|
-
CONF = cfg.CONF
|
12
|
-
LOG = logging.getLogger("APRSD")
|
13
|
-
|
14
|
-
|
15
|
-
class APRSDStoreThread(APRSDThread):
|
16
|
-
"""save object store instances to disk periodically."""
|
17
|
-
|
18
|
-
save_interval = 10
|
19
|
-
|
20
|
-
def __init__(self, obj_list: List[objectstore.ObjectStoreMixin]):
|
21
|
-
super().__init__("STORE")
|
22
|
-
self.obj_list = obj_list
|
23
|
-
|
24
|
-
def loop(self):
|
25
|
-
if self.loop_interval % self.save_interval == 0:
|
26
|
-
for obj in self.obj_list:
|
27
|
-
LOG.debug(f"Saving {obj.__class__.__name__}")
|
28
|
-
obj.save()
|
29
|
-
time.sleep(1)
|
30
|
-
return True
|
aprsd/utils/converters.py
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
from datafiles import converters
|
2
|
-
from datetime import datetime
|
3
|
-
|
4
|
-
|
5
|
-
class MyDateTime(converters.Converter, datetime):
|
6
|
-
|
7
|
-
@classmethod
|
8
|
-
def to_preserialization_data(cls, python_value, **kwargs):
|
9
|
-
# Convert `datetime` to a value that can be serialized
|
10
|
-
return python_value.isoformat()
|
11
|
-
|
12
|
-
@classmethod
|
13
|
-
def to_python_value(cls, deserialized_data, **kwargs):
|
14
|
-
# Convert file value back into a `datetime` object
|
15
|
-
return datetime.fromisoformat(deserialized_data)
|
@@ -1,57 +0,0 @@
|
|
1
|
-
/* Root element */
|
2
|
-
.json-document {
|
3
|
-
padding: 1em 2em;
|
4
|
-
}
|
5
|
-
|
6
|
-
/* Syntax highlighting for JSON objects */
|
7
|
-
ul.json-dict, ol.json-array {
|
8
|
-
list-style-type: none;
|
9
|
-
margin: 0 0 0 1px;
|
10
|
-
border-left: 1px dotted #ccc;
|
11
|
-
padding-left: 2em;
|
12
|
-
}
|
13
|
-
.json-string {
|
14
|
-
color: #0B7500;
|
15
|
-
}
|
16
|
-
.json-literal {
|
17
|
-
color: #1A01CC;
|
18
|
-
font-weight: bold;
|
19
|
-
}
|
20
|
-
|
21
|
-
/* Toggle button */
|
22
|
-
a.json-toggle {
|
23
|
-
position: relative;
|
24
|
-
color: inherit;
|
25
|
-
text-decoration: none;
|
26
|
-
}
|
27
|
-
a.json-toggle:focus {
|
28
|
-
outline: none;
|
29
|
-
}
|
30
|
-
a.json-toggle:before {
|
31
|
-
font-size: 1.1em;
|
32
|
-
color: #c0c0c0;
|
33
|
-
content: "\25BC"; /* down arrow */
|
34
|
-
position: absolute;
|
35
|
-
display: inline-block;
|
36
|
-
width: 1em;
|
37
|
-
text-align: center;
|
38
|
-
line-height: 1em;
|
39
|
-
left: -1.2em;
|
40
|
-
}
|
41
|
-
a.json-toggle:hover:before {
|
42
|
-
color: #aaa;
|
43
|
-
}
|
44
|
-
a.json-toggle.collapsed:before {
|
45
|
-
/* Use rotated down arrow, prevents right arrow appearing smaller than down arrow in some browsers */
|
46
|
-
transform: rotate(-90deg);
|
47
|
-
}
|
48
|
-
|
49
|
-
/* Collapsable placeholder links */
|
50
|
-
a.json-placeholder {
|
51
|
-
color: #aaa;
|
52
|
-
padding: 0 1em;
|
53
|
-
text-decoration: none;
|
54
|
-
}
|
55
|
-
a.json-placeholder:hover {
|
56
|
-
text-decoration: underline;
|
57
|
-
}
|
@@ -1,158 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* jQuery json-viewer
|
3
|
-
* @author: Alexandre Bodelot <alexandre.bodelot@gmail.com>
|
4
|
-
* @link: https://github.com/abodelot/jquery.json-viewer
|
5
|
-
*/
|
6
|
-
(function($) {
|
7
|
-
|
8
|
-
/**
|
9
|
-
* Check if arg is either an array with at least 1 element, or a dict with at least 1 key
|
10
|
-
* @return boolean
|
11
|
-
*/
|
12
|
-
function isCollapsable(arg) {
|
13
|
-
return arg instanceof Object && Object.keys(arg).length > 0;
|
14
|
-
}
|
15
|
-
|
16
|
-
/**
|
17
|
-
* Check if a string represents a valid url
|
18
|
-
* @return boolean
|
19
|
-
*/
|
20
|
-
function isUrl(string) {
|
21
|
-
var urlRegexp = /^(https?:\/\/|ftps?:\/\/)?([a-z0-9%-]+\.){1,}([a-z0-9-]+)?(:(\d{1,5}))?(\/([a-z0-9\-._~:/?#[\]@!$&'()*+,;=%]+)?)?$/i;
|
22
|
-
return urlRegexp.test(string);
|
23
|
-
}
|
24
|
-
|
25
|
-
/**
|
26
|
-
* Transform a json object into html representation
|
27
|
-
* @return string
|
28
|
-
*/
|
29
|
-
function json2html(json, options) {
|
30
|
-
var html = '';
|
31
|
-
if (typeof json === 'string') {
|
32
|
-
// Escape tags and quotes
|
33
|
-
json = json
|
34
|
-
.replace(/&/g, '&')
|
35
|
-
.replace(/</g, '<')
|
36
|
-
.replace(/>/g, '>')
|
37
|
-
.replace(/'/g, ''')
|
38
|
-
.replace(/"/g, '"');
|
39
|
-
|
40
|
-
if (options.withLinks && isUrl(json)) {
|
41
|
-
html += '<a href="' + json + '" class="json-string" target="_blank">' + json + '</a>';
|
42
|
-
} else {
|
43
|
-
// Escape double quotes in the rendered non-URL string.
|
44
|
-
json = json.replace(/"/g, '\\"');
|
45
|
-
html += '<span class="json-string">"' + json + '"</span>';
|
46
|
-
}
|
47
|
-
} else if (typeof json === 'number') {
|
48
|
-
html += '<span class="json-literal">' + json + '</span>';
|
49
|
-
} else if (typeof json === 'boolean') {
|
50
|
-
html += '<span class="json-literal">' + json + '</span>';
|
51
|
-
} else if (json === null) {
|
52
|
-
html += '<span class="json-literal">null</span>';
|
53
|
-
} else if (json instanceof Array) {
|
54
|
-
if (json.length > 0) {
|
55
|
-
html += '[<ol class="json-array">';
|
56
|
-
for (var i = 0; i < json.length; ++i) {
|
57
|
-
html += '<li>';
|
58
|
-
// Add toggle button if item is collapsable
|
59
|
-
if (isCollapsable(json[i])) {
|
60
|
-
html += '<a href class="json-toggle"></a>';
|
61
|
-
}
|
62
|
-
html += json2html(json[i], options);
|
63
|
-
// Add comma if item is not last
|
64
|
-
if (i < json.length - 1) {
|
65
|
-
html += ',';
|
66
|
-
}
|
67
|
-
html += '</li>';
|
68
|
-
}
|
69
|
-
html += '</ol>]';
|
70
|
-
} else {
|
71
|
-
html += '[]';
|
72
|
-
}
|
73
|
-
} else if (typeof json === 'object') {
|
74
|
-
var keyCount = Object.keys(json).length;
|
75
|
-
if (keyCount > 0) {
|
76
|
-
html += '{<ul class="json-dict">';
|
77
|
-
for (var key in json) {
|
78
|
-
if (Object.prototype.hasOwnProperty.call(json, key)) {
|
79
|
-
html += '<li>';
|
80
|
-
var keyRepr = options.withQuotes ?
|
81
|
-
'<span class="json-string">"' + key + '"</span>' : key;
|
82
|
-
// Add toggle button if item is collapsable
|
83
|
-
if (isCollapsable(json[key])) {
|
84
|
-
html += '<a href class="json-toggle">' + keyRepr + '</a>';
|
85
|
-
} else {
|
86
|
-
html += keyRepr;
|
87
|
-
}
|
88
|
-
html += ': ' + json2html(json[key], options);
|
89
|
-
// Add comma if item is not last
|
90
|
-
if (--keyCount > 0) {
|
91
|
-
html += ',';
|
92
|
-
}
|
93
|
-
html += '</li>';
|
94
|
-
}
|
95
|
-
}
|
96
|
-
html += '</ul>}';
|
97
|
-
} else {
|
98
|
-
html += '{}';
|
99
|
-
}
|
100
|
-
}
|
101
|
-
return html;
|
102
|
-
}
|
103
|
-
|
104
|
-
/**
|
105
|
-
* jQuery plugin method
|
106
|
-
* @param json: a javascript object
|
107
|
-
* @param options: an optional options hash
|
108
|
-
*/
|
109
|
-
$.fn.jsonViewer = function(json, options) {
|
110
|
-
// Merge user options with default options
|
111
|
-
options = Object.assign({}, {
|
112
|
-
collapsed: false,
|
113
|
-
rootCollapsable: true,
|
114
|
-
withQuotes: false,
|
115
|
-
withLinks: true
|
116
|
-
}, options);
|
117
|
-
|
118
|
-
// jQuery chaining
|
119
|
-
return this.each(function() {
|
120
|
-
|
121
|
-
// Transform to HTML
|
122
|
-
var html = json2html(json, options);
|
123
|
-
if (options.rootCollapsable && isCollapsable(json)) {
|
124
|
-
html = '<a href class="json-toggle"></a>' + html;
|
125
|
-
}
|
126
|
-
|
127
|
-
// Insert HTML in target DOM element
|
128
|
-
$(this).html(html);
|
129
|
-
$(this).addClass('json-document');
|
130
|
-
|
131
|
-
// Bind click on toggle buttons
|
132
|
-
$(this).off('click');
|
133
|
-
$(this).on('click', 'a.json-toggle', function() {
|
134
|
-
var target = $(this).toggleClass('collapsed').siblings('ul.json-dict, ol.json-array');
|
135
|
-
target.toggle();
|
136
|
-
if (target.is(':visible')) {
|
137
|
-
target.siblings('.json-placeholder').remove();
|
138
|
-
} else {
|
139
|
-
var count = target.children('li').length;
|
140
|
-
var placeholder = count + (count > 1 ? ' items' : ' item');
|
141
|
-
target.after('<a href class="json-placeholder">' + placeholder + '</a>');
|
142
|
-
}
|
143
|
-
return false;
|
144
|
-
});
|
145
|
-
|
146
|
-
// Simulate click on toggle button when placeholder is clicked
|
147
|
-
$(this).on('click', 'a.json-placeholder', function() {
|
148
|
-
$(this).siblings('a.json-toggle').click();
|
149
|
-
return false;
|
150
|
-
});
|
151
|
-
|
152
|
-
if (options.collapsed == true) {
|
153
|
-
// Trigger click to collapse all nodes
|
154
|
-
$(this).find('a.json-toggle').click();
|
155
|
-
}
|
156
|
-
});
|
157
|
-
};
|
158
|
-
})(jQuery);
|
@@ -1,57 +0,0 @@
|
|
1
|
-
/* Root element */
|
2
|
-
.json-document {
|
3
|
-
padding: 1em 2em;
|
4
|
-
}
|
5
|
-
|
6
|
-
/* Syntax highlighting for JSON objects */
|
7
|
-
ul.json-dict, ol.json-array {
|
8
|
-
list-style-type: none;
|
9
|
-
margin: 0 0 0 1px;
|
10
|
-
border-left: 1px dotted #ccc;
|
11
|
-
padding-left: 2em;
|
12
|
-
}
|
13
|
-
.json-string {
|
14
|
-
color: #0B7500;
|
15
|
-
}
|
16
|
-
.json-literal {
|
17
|
-
color: #1A01CC;
|
18
|
-
font-weight: bold;
|
19
|
-
}
|
20
|
-
|
21
|
-
/* Toggle button */
|
22
|
-
a.json-toggle {
|
23
|
-
position: relative;
|
24
|
-
color: inherit;
|
25
|
-
text-decoration: none;
|
26
|
-
}
|
27
|
-
a.json-toggle:focus {
|
28
|
-
outline: none;
|
29
|
-
}
|
30
|
-
a.json-toggle:before {
|
31
|
-
font-size: 1.1em;
|
32
|
-
color: #c0c0c0;
|
33
|
-
content: "\25BC"; /* down arrow */
|
34
|
-
position: absolute;
|
35
|
-
display: inline-block;
|
36
|
-
width: 1em;
|
37
|
-
text-align: center;
|
38
|
-
line-height: 1em;
|
39
|
-
left: -1.2em;
|
40
|
-
}
|
41
|
-
a.json-toggle:hover:before {
|
42
|
-
color: #aaa;
|
43
|
-
}
|
44
|
-
a.json-toggle.collapsed:before {
|
45
|
-
/* Use rotated down arrow, prevents right arrow appearing smaller than down arrow in some browsers */
|
46
|
-
transform: rotate(-90deg);
|
47
|
-
}
|
48
|
-
|
49
|
-
/* Collapsable placeholder links */
|
50
|
-
a.json-placeholder {
|
51
|
-
color: #aaa;
|
52
|
-
padding: 0 1em;
|
53
|
-
text-decoration: none;
|
54
|
-
}
|
55
|
-
a.json-placeholder:hover {
|
56
|
-
text-decoration: underline;
|
57
|
-
}
|