Uses 12447K of libraries. Click here for Pure Java version (18775L/98K).
1 | !7 |
2 | |
3 | concept Screen {
|
4 | S screenID; |
5 | long lastSeen = now(); |
6 | |
7 | S query; |
8 | |
9 | transient S status; |
10 | |
11 | // the ongoing search (if any) |
12 | transient LC_ParallelSearch search; |
13 | |
14 | transient Lock lock = lock(); |
15 | |
16 | toString { ret "Screen " + screenID; }
|
17 | } |
18 | |
19 | cmodule2 BEALiveInput > DynPrintLogAndEnabled {
|
20 | switchable int statusUpdateInterval = 100; |
21 | |
22 | transient Map<virtual WebSocket, Screen> webSockets = syncWeakHashMap(); |
23 | |
24 | transient Set<virtual WebSocket> liveSearches = weakHashSet(); |
25 | |
26 | start {
|
27 | dbIndexing(Screen, 'screenID); |
28 | dm_doEvery(statusUpdateInterval, r updateStati); |
29 | } |
30 | |
31 | set flag NoNanoHTTPD. |
32 | O html(IWebRequest request) { try {
|
33 | //try object redirectToHTTPS(request); |
34 | |
35 | S uri = request.uri(); |
36 | SS params = request.params(); |
37 | print(+params); |
38 | |
39 | S cookie = request.cookie(); |
40 | print("cookie", cookie);
|
41 | |
42 | new Matches m; |
43 | |
44 | if (eqic(uri, "/about")) |
45 | ret serveText(unindent_mls([[ |
46 | What is the BEA Live Input? |
47 | |
48 | Bla bla bla. Also, bla. |
49 | ]])); |
50 | |
51 | if (eqic(uri, "/websockettest")) {
|
52 | ret htitle("Web Socket Test")
|
53 | + hreconnectingWebSockets() |
54 | + hloadjquery() |
55 | + hdiv("bla<br>", id := "dynamic", style := "font-size: 40px")
|
56 | + hjavascript([[ |
57 | var ws = new ReconnectingWebSocket("wss://wikify.live/test");
|
58 | ws.onopen = function(event) {
|
59 | ws.send("here i am");
|
60 | $("#dynamic").append("Streaming...<br>");
|
61 | }; |
62 | ws.onmessage = function(event) {
|
63 | $("#dynamic").html(event.data);
|
64 | document.title = event.data; |
65 | }; |
66 | ]]); |
67 | } |
68 | |
69 | // home page |
70 | S query = ""; |
71 | if (swic(uri, "/search/", m)) |
72 | query = takeFirst(1000, urldecode(m.rest())); |
73 | else // now we actually accept wikify.live/{any search term}
|
74 | query = urldecode(dropSlashPrefix(uri)); |
75 | |
76 | ret hhtml(hmobilefix() + hhead( |
77 | htitle("BEA Live Input [dev.]")
|
78 | + hsansserif() |
79 | + hstyle([[ |
80 | .queryContainer {
|
81 | height: 100%; |
82 | width: 100%; |
83 | |
84 | display: flex; |
85 | flex-flow: column; |
86 | } |
87 | |
88 | input.query {
|
89 | font-size: 40px; |
90 | padding: 20px; |
91 | width: 400px; |
92 | height: 2em; |
93 | |
94 | position: relative; |
95 | left: 50%; |
96 | transform: translate(-50%, 0); |
97 | flex: .01 1 auto; |
98 | } |
99 | |
100 | .filler {
|
101 | flex: .5 1 auto; |
102 | } |
103 | |
104 | .filler.ontop {
|
105 | flex: .01 1 auto; |
106 | } |
107 | |
108 | input.query.ontop {
|
109 | transform: translate(-50%, 0); |
110 | margin-top: 20px; |
111 | font-size: 20px; |
112 | } |
113 | |
114 | .searching {
|
115 | display: none; |
116 | } |
117 | |
118 | .searching.show {
|
119 | display: block; |
120 | position: relative; |
121 | left: 50%; |
122 | margin-left: -45px; |
123 | } |
124 | |
125 | .results {
|
126 | font-family: monospace; |
127 | white-space: pre; /* no word-wrapping */ |
128 | overflow: auto; /* scrollbars */ |
129 | margin-top: 20px; |
130 | border: 2px solid black; |
131 | padding: 0px; |
132 | display: block; |
133 | flex: .01 1 auto; |
134 | opacity: 0%; |
135 | width: 1px; |
136 | margin-left: auto; |
137 | margin-right: auto; |
138 | } |
139 | |
140 | .results.ontop {
|
141 | padding: 20px; |
142 | flex: 1 0 0; |
143 | opacity: 100%; |
144 | width: calc(100% - 50px); |
145 | left: 0; |
146 | transform: none; |
147 | } |
148 | |
149 | .about {
|
150 | margin-bottom: 10px; |
151 | font-size: 12px; |
152 | text-align: center; |
153 | margin-left: auto; |
154 | margin-right: auto; |
155 | } |
156 | |
157 | .status {
|
158 | display: none; |
159 | margin-top: 10px; |
160 | font-size: 12px; |
161 | } |
162 | |
163 | .status.ontop {
|
164 | display: block; |
165 | margin-left: auto; |
166 | margin-right: auto; |
167 | } |
168 | |
169 | .query, .results, .filler {
|
170 | transition: all 1s; |
171 | } |
172 | ]]) |
173 | + hLoadJQuery2() |
174 | + hreconnectingWebSockets()) |
175 | + hbody(hdiv( |
176 | hdiv("", class := "filler")
|
177 | + hdiv("AI bla bla<br>"
|
178 | + "By " + ahref("https://BotCompany.de", "BotCompany") + "."
|
179 | + targetBlank("/about", "More info."), class := "about")
|
180 | + hinput("", value := query, autofocus := true, class := "query", placeholder := "TYPE ANYTHING")
|
181 | + hdiv("", class := "status")
|
182 | + hdiv( |
183 | hdiv("", class := "actualResults")
|
184 | + hdiv(himgsnippet(#1102949, title := "Searching..."), class := "searching"), |
185 | class := "results") |
186 | + hdiv("", class := "filler"),
|
187 | class := "queryContainer") |
188 | + hjs_urlencode() |
189 | + hjs([[ |
190 | function queryUpdate() {
|
191 | var sel = $("input.query, .queryContainer, .results, .filler, .status");
|
192 | var q = $("input.query").val();
|
193 | if (q == "") {
|
194 | $(".actualResults").html("");
|
195 | sel.removeClass("ontop");
|
196 | } else {
|
197 | sel.addClass("ontop");
|
198 | } |
199 | if (history.replaceState) |
200 | history.replaceState(null, "Wikify.LIVE", q == "" ? "/" : "/search/" + urlencode(q)); |
201 | sendQuery(); |
202 | } |
203 | |
204 | $("input.query").on('change keydown paste input', queryUpdate);
|
205 | var screenID = Math.random().toString(36).substr(2, 9); |
206 | var wsURL = "wss://wikify.live/search?screen=" + urlencode(screenID); |
207 | console.log("WS: " + wsURL);
|
208 | var ws = new ReconnectingWebSocket(wsURL); |
209 | var wsReady = false; |
210 | var querySent = ""; |
211 | var maxQueryLength = 1000; |
212 | |
213 | function wsSend(text) {
|
214 | if (wsReady) {
|
215 | console.log("wsSend: " + text);
|
216 | ws.send(text); |
217 | } else |
218 | console.log("wsNotReady: " + text);
|
219 | } |
220 | |
221 | function sendQuery() {
|
222 | var q = $("input.query").val().substring(0, maxQueryLength);
|
223 | if (querySent != q) {
|
224 | querySent = q; |
225 | wsSend(JSON.stringify({ q: q }));
|
226 | } |
227 | } |
228 | |
229 | ws.onopen = function(event) {
|
230 | wsReady = true; |
231 | ws.onmessage = function(event) {
|
232 | var text = event.data; |
233 | console.log("Got text: " + text);
|
234 | var data = JSON.parse(text); |
235 | if ('clearResults' in data)
|
236 | $(".actualResults").html("");
|
237 | if ('searching' in data)
|
238 | $(".searching").addClass("show");
|
239 | if ('result' in data)
|
240 | $(".actualResults").append(data.result + "<br>");
|
241 | if ('searchCancelled' in data)
|
242 | $(".searching").removeClass("show");
|
243 | if ('searchDone' in data) {
|
244 | $(".actualResults").append("<br>[SEARCH DONE]<br>");
|
245 | $(".searching").removeClass("show");
|
246 | } |
247 | if ('status' in data)
|
248 | $(".status").html(data.status);
|
249 | }; |
250 | queryUpdate(); |
251 | }; |
252 | ]]) |
253 | )); |
254 | } catch e { printStackTrace(e); throw rethrow(e); }
|
255 | } |
256 | |
257 | class Request {
|
258 | S cookie; |
259 | } |
260 | |
261 | void cleanMeUp_webSockets {
|
262 | for (virtual WebSocket ws : cloneAndClearKeys(webSockets)) {
|
263 | print("Closing WebSocket");
|
264 | close((AutoCloseable) ws); |
265 | } |
266 | } |
267 | |
268 | void cleanMeUp_searches {
|
269 | for (virtual WebSocket ws : cloneAndClearList(liveSearches)) |
270 | cancelSearch_noLock(webSockets.get(ws)); |
271 | } |
272 | |
273 | // API for Eleu |
274 | |
275 | void setEleu(O eleu) {
|
276 | setOptMC(mainBot := eleu); |
277 | } |
278 | |
279 | void handleWebSocket(virtual WebSocket ws) {
|
280 | print("Got new websocket!");
|
281 | set(ws, onClose := r { webSockets.remove(ws) });
|
282 | |
283 | set(ws, onOpen := rEnter {
|
284 | S uri = cast rcall getUri(ws); |
285 | |
286 | print("WebSocket opened! uri: " + uri);
|
287 | |
288 | SS params = cast rcall getParms(ws); |
289 | S screenID = takeFirst(params.get("screen"), 100);
|
290 | Screen screen = uniq Screen(+screenID); |
291 | screen.status = null; |
292 | webSockets.put(ws, screen); |
293 | cset(screen, lastSeen := now()); |
294 | |
295 | print("Screen ID: " + screenID);
|
296 | |
297 | setFieldToIVF1Proxy(ws, onMessage := msg -> {
|
298 | temp enter(); |
299 | pcall {
|
300 | S text = cast rcall getTextPayload(msg); |
301 | print("Got msg: " + text);
|
302 | |
303 | Map json = jsonDecodeMap(text); |
304 | S q = upper((S) json.get("q"));
|
305 | if (q != null && neq(screen.query, q)) {
|
306 | temp tempLock("start search", screen.lock);
|
307 | |
308 | cancelSearch_noLock(screen); |
309 | cset(screen, query := q); |
310 | if (nempty(q)) |
311 | startSearch_noLock(ws, screen, q); |
312 | } |
313 | } |
314 | }); |
315 | |
316 | if (eq(uri, "/test")) thread {
|
317 | repeat 100 {
|
318 | call(ws, 'send, "It is " + localTimeWithMilliseconds()); |
319 | sleep(100); |
320 | } |
321 | } |
322 | |
323 | if (eq(uri, "/search")) {
|
324 | } |
325 | }); |
326 | } |
327 | |
328 | void cancelSearch_noLock(Screen screen) {
|
329 | if (screen == null) ret; |
330 | close(screen.search); |
331 | screen.search = null; |
332 | } |
333 | |
334 | void startSearch_noLock(virtual WebSocket ws, Screen screen, S query) {
|
335 | cset(screen, +query); |
336 | call(ws, "send", jsonEncode(litorderedmap("clearResults", true)));
|
337 | |
338 | if (empty(query)) ret; |
339 | |
340 | call(ws, "send", jsonEncode(litorderedmap("searching", true)));
|
341 | |
342 | print("Starting search for " + quote(query) + " for " + screen);
|
343 | /*LC_ParallelSearch search = new(searchers, query); |
344 | screen.search = search; |
345 | liveSearches.add(ws); |
346 | search.enter = lambda0 enter; |
347 | search.maxResults = defaultResults; |
348 | search.onResult = (iSearcher, iFile, position) -> {
|
349 | temp tempLock("onResult", screen.lock);
|
350 | if (screen.search != search) ret; |
351 | try {
|
352 | //print("Sending result");
|
353 | S status = search.status_html(); |
354 | call(ws, "send", jsonEncode(litorderedmap( |
355 | result := renderResult(iSearcher, iFile, position, query, screen.search.startTime), |
356 | +status))); |
357 | //print("Sent result");
|
358 | } catch print e {
|
359 | cancelSearch_noLock(screen); |
360 | } |
361 | }; |
362 | search.onSearchDone = r {
|
363 | liveSearches.remove(ws); |
364 | print("Search done. " + screen.search + " / " + search);
|
365 | temp tempLock("onSearchDone", screen.lock);
|
366 | if (screen.search != search) ret; |
367 | updateStatus(ws, screen); |
368 | print("Cancelling search");
|
369 | cancelSearch_noLock(screen); |
370 | print("Sending searchDone");
|
371 | call(ws, "send", jsonEncode(litorderedmap( |
372 | search.completed() ? "searchDone" : "searchCancelled", true))); |
373 | print("Sent searchDone");
|
374 | }; |
375 | search.start();*/ |
376 | } |
377 | |
378 | void updateStati enter {
|
379 | for (virtual WebSocket ws : cloneList(liveSearches)) pcall {
|
380 | Screen screen = webSockets.get(ws); |
381 | updateStatus(ws, screen); |
382 | } |
383 | } |
384 | |
385 | void updateStatus(virtual WebSocket ws, Screen screen) {
|
386 | if (screen == null) ret; |
387 | /*LC_ParallelSearch search = screen.search; |
388 | if (search == null) ret; |
389 | S status = search.status_html(); |
390 | if (neq(status, screen.status)) {
|
391 | screen.status = status; |
392 | call(ws, "send", jsonEncode(litorderedmap(status := screen.status))); |
393 | }*/ |
394 | } |
395 | } |
Began life as a copy of #1029430
download show line numbers debug dex old transpilations
Travelled to 4 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, vouqrxazstgt
No comments. add comment
| Snippet ID: | #1030819 |
| Snippet name: | BEA Live Input [dev.] |
| Eternal ID of this version: | #1030819/10 |
| Text MD5: | a2d03d42941d69da4bb682023afdd7d3 |
| Transpilation MD5: | e0e9be9e77162150c598de135b54f6b7 |
| Author: | stefan |
| Category: | javax |
| Type: | JavaX source code (Dynamic Module) |
| Public (visible to everyone): | Yes |
| Archived (hidden from active list): | No |
| Created/modified: | 2021-07-14 18:44:26 |
| Source code size: | 11481 bytes / 395 lines |
| Pitched / IR pitched: | No / No |
| Views / Downloads: | 444 / 65484 |
| Version history: | 9 change(s) |
| Referenced in: | [show references] |