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: | 203 / 65182 |
Version history: | 9 change(s) |
Referenced in: | [show references] |