Fossil SCM

Remove the lingering JS code for the /chat-search page removed in [e1f6c7f589b4].

stephan 2024-07-01 08:32 fts5-chat-search
Commit 39496a32e64e6c06077b18239325e54cd773d1e1a18538807a2b78e659a1f2d9
D src/fossil.page.chatsearch.js
-481
--- a/src/fossil.page.chatsearch.js
+++ b/src/fossil.page.chatsearch.js
@@ -1,481 +0,0 @@
1
-/*
2
-** This file contains the client-side implementation of fossil's
3
-** /chat-search application.
4
-*/
5
-window.fossil.onPageLoad(function(){
6
-
7
- const F = window.fossil, D = F.dom;
8
- const E1 = function(selector){
9
- const e = document.querySelector(selector);
10
- if(!e) throw new Error("missing required DOM element: "+selector);
11
- return e;
12
- };
13
-
14
-/************************************************************************/
15
-/************************************************************************/
16
-/************************************************************************/
17
-
18
- /**
19
- Custom widget type for rendering messages (one message per
20
- instance). These are modelled after FIELDSET elements but we
21
- don't use FIELDSET because of cross-browser inconsistencies in
22
- features of the FIELDSET/LEGEND combination, e.g. inability to
23
- align legends via CSS in Firefox and clicking-related
24
- deficiencies in Safari.
25
- */
26
- var MessageWidget = (function(){
27
- /**
28
- Constructor. If passed an argument, it is passed to
29
- this.setMessage() after initialization.
30
- */
31
- const cf = function(){
32
- this.e = {
33
- body: D.addClass(D.div(), 'message-widget'),
34
- tab: D.addClass(D.div(), 'message-widget-tab'),
35
- content: D.addClass(D.div(), 'message-widget-content')
36
- };
37
- D.append(this.e.body, this.e.tab, this.e.content);
38
- this.e.tab.setAttribute('role', 'button');
39
- if(arguments.length){
40
- this.setMessage(arguments[0]);
41
- }
42
- };
43
-
44
- /**
45
- Returns true if this page believes it can embed a view of the
46
- file wrapped by the given message object, else returns false.
47
- */
48
- const canEmbedFile = function f(msg){
49
- if(!f.$rx){
50
- f.$rx = /\.((html?)|(txt)|(md)|(wiki)|(pikchr))$/i;
51
- f.$specificTypes = [
52
- 'text/plain',
53
- 'text/html',
54
- 'text/x-markdown',
55
- /* Firefox sends text/markdown when uploading .md files */
56
- 'text/markdown',
57
- 'text/x-pikchr',
58
- 'text/x-fossil-wiki'
59
- // add more as we discover which ones Firefox won't
60
- // force the user to try to download.
61
- ];
62
- }
63
- if(msg.fmime){
64
- if(msg.fmime.startsWith("image/")
65
- || f.$specificTypes.indexOf(msg.fmime)>=0){
66
- return true;
67
- }
68
- }
69
- return (msg.fname && f.$rx.test(msg.fname));
70
- };
71
-
72
- /**
73
- Returns true if the given message object "should"
74
- be embedded in fossil-rendered form instead of
75
- raw content form. This is only intended to be passed
76
- message objects for which canEmbedFile() returns true.
77
- */
78
- const shouldWikiRenderEmbed = function f(msg){
79
- if(!f.$rx){
80
- f.$rx = /\.((md)|(wiki)|(pikchr))$/i;
81
- f.$specificTypes = [
82
- 'text/x-markdown',
83
- 'text/markdown' /* Firefox-uploaded md files */,
84
- 'text/x-pikchr',
85
- 'text/x-fossil-wiki'
86
- // add more as we discover which ones Firefox won't
87
- // force the user to try to download.
88
- ];
89
- }
90
- if(msg.fmime){
91
- if(f.$specificTypes.indexOf(msg.fmime)>=0) return true;
92
- }
93
- return msg.fname && f.$rx.test(msg.fname);
94
- };
95
-
96
- const adjustIFrameSize = function(msgObj){
97
- const iframe = msgObj.e.iframe;
98
- const body = iframe.contentWindow.document.querySelector('body');
99
- if(body && !body.style.fontSize){
100
- /** _Attempt_ to force the iframe to inherit the message's text size
101
- if the body has no explicit size set. On desktop systems
102
- the size is apparently being inherited in that case, but on mobile
103
- not. */
104
- body.style.fontSize = window.getComputedStyle(msgObj.e.content);
105
- }
106
- if('' === iframe.style.maxHeight){
107
- /* Resize iframe height to fit the content. Workaround: if we
108
- adjust the iframe height while it's hidden then its height
109
- is 0, so we must briefly unhide it. */
110
- const isHidden = iframe.classList.contains('hidden');
111
- if(isHidden) D.removeClass(iframe, 'hidden');
112
- iframe.style.maxHeight = iframe.style.height
113
- = iframe.contentWindow.document.documentElement.scrollHeight + 'px';
114
- if(isHidden) D.addClass(iframe, 'hidden');
115
- }
116
- };
117
-
118
- cf.prototype = {
119
- scrollIntoView: function(){
120
- this.e.content.scrollIntoView();
121
- },
122
- setMessage: function(m){
123
- const ds = this.e.body.dataset;
124
- ds.timestamp = m.mtime;
125
- ds.lmtime = m.lmtime;
126
- ds.msgid = m.msgid;
127
- ds.xfrom = m.xfrom || '';
128
-
129
- if(m.uclr){
130
- this.e.content.style.backgroundColor = m.uclr;
131
- this.e.tab.style.backgroundColor = m.uclr;
132
- }
133
- const d = new Date(m.mtime);
134
- D.clearElement(this.e.tab);
135
- var contentTarget = this.e.content;
136
- var eXFrom /* element holding xfrom name */;
137
- var eXFrom = D.append(D.addClass(D.span(), 'xfrom'), m.xfrom);
138
- const wrapper = D.append(
139
- D.span(), eXFrom,
140
- D.text(" #",(m.msgid||'???'),' @ ',d.toLocaleString()));
141
- D.append(this.e.tab, wrapper);
142
-
143
- if( m.xfrom && m.fsize>0 ){
144
- if( m.fmime
145
- && m.fmime.startsWith("image/")
146
- /* && Chat.settings.getBool('images-inline',true) */
147
- ){
148
- const extension = m.fname.split('.').pop();
149
- contentTarget.appendChild(D.img("chat-download/" + m.msgid +(
150
- extension ? ('.'+extension) : ''/*So that IMG tag mimetype guessing works*/
151
- )));
152
- ds.hasImage = 1;
153
- }else{
154
- // Add a download link.
155
- const downloadUri = window.fossil.rootPath+
156
- 'chat-download/' + m.msgid+'/'+encodeURIComponent(m.fname);
157
- const w = D.addClass(D.div(), 'attachment-link');
158
- const a = D.a(downloadUri,
159
- // ^^^ add m.fname to URL to cause downloaded file to have that name.
160
- "(" + m.fname + " " + m.fsize + " bytes)"
161
- )
162
- D.attr(a,'target','_blank');
163
- D.append(w, a);
164
- if(canEmbedFile(m)){
165
- /* Add an option to embed HTML attachments in an iframe. The primary
166
- use case is attached diffs. */
167
- const shouldWikiRender = shouldWikiRenderEmbed(m);
168
- const downloadArgs = shouldWikiRender ? '?render' : '';
169
- D.addClass(contentTarget, 'wide');
170
- const embedTarget = this.e.content;
171
- const self = this;
172
- const btnEmbed = D.attr(D.checkbox("1", false), 'id',
173
- 'embed-'+ds.msgid);
174
- const btnLabel = D.label(btnEmbed, shouldWikiRender
175
- ? "Embed (fossil-rendered)" : "Embed");
176
- /* Maintenance reminder: do not disable the toggle
177
- button while the content is loading because that will
178
- cause it to get stuck in disabled mode if the browser
179
- decides that loading the content should prompt the
180
- user to download it, rather than embed it in the
181
- iframe. */
182
- btnEmbed.addEventListener('change',function(){
183
- if(self.e.iframe){
184
- if(btnEmbed.checked){
185
- D.removeClass(self.e.iframe, 'hidden');
186
- if(self.e.$iframeLoaded) adjustIFrameSize(self);
187
- }
188
- else D.addClass(self.e.iframe, 'hidden');
189
- return;
190
- }
191
- const iframe = self.e.iframe = document.createElement('iframe');
192
- D.append(embedTarget, iframe);
193
- iframe.addEventListener('load', function(){
194
- self.e.$iframeLoaded = true;
195
- adjustIFrameSize(self);
196
- });
197
- iframe.setAttribute('src', downloadUri + downloadArgs);
198
- });
199
- D.append(w, btnEmbed, btnLabel);
200
- }
201
- contentTarget.appendChild(w);
202
- }
203
- }
204
- if(m.xmsg){
205
- if(m.fsize>0){
206
- /* We have file/image content, so need another element for
207
- the message text. */
208
- contentTarget = D.div();
209
- D.append(this.e.content, contentTarget);
210
- }
211
- D.addClass(contentTarget, 'content-target'
212
- /*target element for the 'toggle text mode' feature*/);
213
- // The m.xmsg text comes from the same server as this script and
214
- // is guaranteed by that server to be "safe" HTML - safe in the
215
- // sense that it is not possible for a malefactor to inject HTML
216
- // or javascript or CSS. The m.xmsg content might contain
217
- // hyperlinks, but otherwise it will be markup-free. See the
218
- // chat_format_to_html() routine in the server for details.
219
- //
220
- // Hence, even though innerHTML is normally frowned upon, it is
221
- // perfectly safe to use in this context.
222
- if(m.xmsg && 'string' !== typeof m.xmsg){
223
- // Used by Chat.reportErrorAsMessage()
224
- D.append(contentTarget, m.xmsg);
225
- }else{
226
- contentTarget.innerHTML = m.xmsg;
227
- // contentTarget.querySelectorAll('a').forEach(addAnchorTargetBlank);
228
- if(F.pikchr){
229
- F.pikchr.addSrcView(contentTarget.querySelectorAll('svg.pikchr'));
230
- }
231
- }
232
- }
233
- //console.debug("tab",this.e.tab);
234
- //console.debug("this.e.tab.firstElementChild",this.e.tab.firstElementChild);
235
- // this.e.tab.firstElementChild.addEventListener('click', this._handleLegendClicked, false);
236
- /*if(eXFrom){
237
- eXFrom.addEventListener('click', ()=>this.e.tab.click(), false);
238
- }*/
239
- return this;
240
- }
241
- };
242
- return cf;
243
- })()/*MessageWidget*/;
244
-
245
-/************************************************************************/
246
-/************************************************************************/
247
-/************************************************************************/
248
-
249
- var MessageSpacer = (function(){
250
- const nMsgContext = 5;
251
- const zUpArrow = '\u25B2';
252
- const zDownArrow = '\u25BC';
253
-
254
- const cf = function(o){
255
-
256
- /* iFirstInTable:
257
- ** msgid of first row in chatfts table.
258
- **
259
- ** iLastInTable:
260
- ** msgid of last row in chatfts table.
261
- **
262
- ** iPrevId:
263
- ** msgid of message immediately above this spacer. Or 0 if this
264
- ** spacer is above all results.
265
- **
266
- ** iNextId:
267
- ** msgid of message immediately below this spacer. Or 0 if this
268
- ** spacer is below all results.
269
- **
270
- ** bIgnoreClick:
271
- ** ignore any clicks if this is true. This is used to ensure there
272
- ** is only ever one request belonging to this widget outstanding
273
- ** at any time.
274
- */
275
- this.o = {
276
- iFirstInTable: o.first,
277
- iLastInTable: o.last,
278
- iPrevId: o.previd,
279
- iNextId: o.nextid,
280
- bIgnoreClick: false,
281
- };
282
-
283
- this.e = {
284
- body: D.addClass(D.div(), 'spacer-widget'),
285
-
286
- above: D.addClass(D.div(), 'spacer-widget-above'),
287
- buttons: D.addClass(D.div(), 'spacer-widget-buttons'),
288
- below: D.addClass(D.div(), 'spacer-widget-below'),
289
-
290
- up: D.button(zDownArrow+' Load '+nMsgContext+' more '+zDownArrow),
291
- down: D.button(zUpArrow+' Load '+nMsgContext+' more '+zUpArrow),
292
- all: D.button('Load More'),
293
- };
294
-
295
- D.addClass(this.e.up, 'up');
296
- D.addClass(this.e.down, 'down');
297
- D.addClass(this.e.all, 'all');
298
-
299
- D.append(this.e.buttons, this.e.up, this.e.down, this.e.all);
300
- D.append(this.e.body, this.e.above, this.e.buttons, this.e.below);
301
-
302
- const ms = this;
303
- this.e.up.addEventListener('click', function(){
304
- ms.load_messages(false);
305
- });
306
- this.e.down.addEventListener('click', function(){
307
- ms.load_messages(true);
308
- });
309
- this.e.all.addEventListener('click', function(){
310
- ms.load_messages( (ms.o.iPrevId==0) );
311
- });
312
-
313
- this.set_button_visibility();
314
- };
315
-
316
- cf.prototype = {
317
- set_button_visibility: function() {
318
- var o = this.o;
319
-
320
- var iPrevId = (o.iPrevId!=0) ? o.iPrevId : o.iFirstInTable-1;
321
- var iNextId = (o.iNextId!=0) ? o.iNextId : o.iLastInTable+1;
322
- var nDiff = (iNextId - iPrevId) - 1;
323
-
324
- D.addClass([this.e.up, this.e.down, this.e.all], 'hidden');
325
-
326
- if( nDiff>0 ){
327
-
328
- if( nDiff>nMsgContext && (o.iPrevId==0 || o.iNextId==0) ){
329
- nDiff = nMsgContext;
330
- }
331
-
332
- if( nDiff<=nMsgContext && o.iPrevId!=0 && o.iNextId!=0 ){
333
- D.removeClass(this.e.all, 'hidden');
334
- this.e.all.innerText = (
335
- zUpArrow + " Load " + nDiff + " more " + zDownArrow
336
- );
337
- }else{
338
- if( o.iPrevId!=0 ) D.removeClass(this.e.up, 'hidden');
339
- if( o.iNextId!=0 ) D.removeClass(this.e.down, 'hidden');
340
- }
341
- }
342
- },
343
-
344
- load_messages: function(bDown) {
345
- var iFirst = 0; /* msgid of first message to fetch */
346
- var nFetch = 0; /* Number of messages to fetch */
347
- var iEof = 0; /* last msgid in spacers range, plus 1 */
348
-
349
- var e = this.e;
350
- var o = this.o;
351
-
352
- if( this.bIgnoreClick ) return;
353
- this.bIgnoreClick = true;
354
-
355
- /* Figure out the required range of messages. */
356
- if( bDown ){
357
- iFirst = this.o.iNextId - nMsgContext;
358
- if( iFirst<this.o.iFirstInTable ){
359
- iFirst = this.o.iFirstInTable;
360
- }
361
- }else{
362
- iFirst = this.o.iPrevId+1;
363
- }
364
- nFetch = nMsgContext;
365
- iEof = (this.o.iNextId > 0) ? this.o.iNextId : this.o.iLastInTable+1;
366
- if( iFirst+nFetch>iEof ){
367
- nFetch = iEof - iFirst;
368
- }
369
-
370
-
371
- const ms = this;
372
- F.fetch("chat-query",{
373
- urlParams:{
374
- q: '',
375
- n: nFetch,
376
- i: iFirst
377
- },
378
- responseType: "json",
379
-
380
- onerror:function(err){
381
- console.error(err);
382
- alert(err.toString());
383
- },
384
-
385
- onload:function(jx){
386
- const firstChildOfBelow = e.below.firstChild;
387
- jx.msgs.forEach((m) => {
388
- var mw = new MessageWidget(m);
389
- if( bDown ){
390
- e.below.insertBefore(mw.e.body, firstChildOfBelow);
391
- }else{
392
- D.append(e.above, mw.e.body);
393
- }
394
- });
395
-
396
- if( bDown ){
397
- o.iNextId -= jx.msgs.length;
398
- }else{
399
- o.iPrevId += jx.msgs.length;
400
- }
401
-
402
- ms.set_button_visibility();
403
- ms.bIgnoreClick = false;
404
- }
405
- });
406
- }
407
- };
408
-
409
- return cf;
410
- })(); /* MessageSpacer */
411
-
412
- /* This is called to submit a search - because the user clicked the
413
- ** search button or pressed Enter in the input box.
414
- */
415
- const submit_search = function() {
416
- const v = E1('#textinput').value;
417
- F.fetch("chat-query",{
418
- urlParams:{
419
- q: v
420
- },
421
- responseType: "json",
422
-
423
- onerror:function(err){
424
- console.error(err);
425
- alert(err.toString());
426
- },
427
-
428
- onload:function(jx){
429
- var res = E1('#results');
430
- var previd = 0;
431
-
432
- D.clearElement(res);
433
- jx.msgs.forEach((m) => {
434
- var mw = new MessageWidget(m);
435
- var spacer = new MessageSpacer({
436
- first: jx.first,
437
- last: jx.last,
438
- previd: previd,
439
- nextid: m.msgid
440
- });
441
-
442
- D.append( res, spacer.e.body );
443
- D.append( res, mw.e.body );
444
-
445
- previd = m.msgid;
446
- });
447
-
448
- if( jx.msgs.length>0 ){
449
- var spacer = new MessageSpacer({
450
- first: jx.first,
451
- last: jx.last,
452
- previd: previd,
453
- nextid: 0
454
- });
455
- D.append( res, spacer.e.body );
456
- } else {
457
- res.innerHTML = '<center><i>No query results</i></center>';
458
- }
459
-
460
- window.scrollTo(0, E1('body').scrollHeight);
461
- }
462
- });
463
- }
464
-
465
- /* Add event listeners to call submit_search() if the user presses Enter
466
- ** or clicks the search button.
467
- */
468
- E1('#searchbutton').addEventListener('click', function(){
469
- submit_search();
470
- });
471
- E1('#textinput').addEventListener('keydown', function(ev){
472
- if( 13==ev.keyCode ){
473
- /* If the key pressed was Enter */
474
- submit_search();
475
- }
476
- });
477
-
478
- /* Focus the input widget */
479
- E1('#textinput').focus();
480
-
481
-});
--- a/src/fossil.page.chatsearch.js
+++ b/src/fossil.page.chatsearch.js
@@ -1,481 +0,0 @@
1 /*
2 ** This file contains the client-side implementation of fossil's
3 ** /chat-search application.
4 */
5 window.fossil.onPageLoad(function(){
6
7 const F = window.fossil, D = F.dom;
8 const E1 = function(selector){
9 const e = document.querySelector(selector);
10 if(!e) throw new Error("missing required DOM element: "+selector);
11 return e;
12 };
13
14 /************************************************************************/
15 /************************************************************************/
16 /************************************************************************/
17
18 /**
19 Custom widget type for rendering messages (one message per
20 instance). These are modelled after FIELDSET elements but we
21 don't use FIELDSET because of cross-browser inconsistencies in
22 features of the FIELDSET/LEGEND combination, e.g. inability to
23 align legends via CSS in Firefox and clicking-related
24 deficiencies in Safari.
25 */
26 var MessageWidget = (function(){
27 /**
28 Constructor. If passed an argument, it is passed to
29 this.setMessage() after initialization.
30 */
31 const cf = function(){
32 this.e = {
33 body: D.addClass(D.div(), 'message-widget'),
34 tab: D.addClass(D.div(), 'message-widget-tab'),
35 content: D.addClass(D.div(), 'message-widget-content')
36 };
37 D.append(this.e.body, this.e.tab, this.e.content);
38 this.e.tab.setAttribute('role', 'button');
39 if(arguments.length){
40 this.setMessage(arguments[0]);
41 }
42 };
43
44 /**
45 Returns true if this page believes it can embed a view of the
46 file wrapped by the given message object, else returns false.
47 */
48 const canEmbedFile = function f(msg){
49 if(!f.$rx){
50 f.$rx = /\.((html?)|(txt)|(md)|(wiki)|(pikchr))$/i;
51 f.$specificTypes = [
52 'text/plain',
53 'text/html',
54 'text/x-markdown',
55 /* Firefox sends text/markdown when uploading .md files */
56 'text/markdown',
57 'text/x-pikchr',
58 'text/x-fossil-wiki'
59 // add more as we discover which ones Firefox won't
60 // force the user to try to download.
61 ];
62 }
63 if(msg.fmime){
64 if(msg.fmime.startsWith("image/")
65 || f.$specificTypes.indexOf(msg.fmime)>=0){
66 return true;
67 }
68 }
69 return (msg.fname && f.$rx.test(msg.fname));
70 };
71
72 /**
73 Returns true if the given message object "should"
74 be embedded in fossil-rendered form instead of
75 raw content form. This is only intended to be passed
76 message objects for which canEmbedFile() returns true.
77 */
78 const shouldWikiRenderEmbed = function f(msg){
79 if(!f.$rx){
80 f.$rx = /\.((md)|(wiki)|(pikchr))$/i;
81 f.$specificTypes = [
82 'text/x-markdown',
83 'text/markdown' /* Firefox-uploaded md files */,
84 'text/x-pikchr',
85 'text/x-fossil-wiki'
86 // add more as we discover which ones Firefox won't
87 // force the user to try to download.
88 ];
89 }
90 if(msg.fmime){
91 if(f.$specificTypes.indexOf(msg.fmime)>=0) return true;
92 }
93 return msg.fname && f.$rx.test(msg.fname);
94 };
95
96 const adjustIFrameSize = function(msgObj){
97 const iframe = msgObj.e.iframe;
98 const body = iframe.contentWindow.document.querySelector('body');
99 if(body && !body.style.fontSize){
100 /** _Attempt_ to force the iframe to inherit the message's text size
101 if the body has no explicit size set. On desktop systems
102 the size is apparently being inherited in that case, but on mobile
103 not. */
104 body.style.fontSize = window.getComputedStyle(msgObj.e.content);
105 }
106 if('' === iframe.style.maxHeight){
107 /* Resize iframe height to fit the content. Workaround: if we
108 adjust the iframe height while it's hidden then its height
109 is 0, so we must briefly unhide it. */
110 const isHidden = iframe.classList.contains('hidden');
111 if(isHidden) D.removeClass(iframe, 'hidden');
112 iframe.style.maxHeight = iframe.style.height
113 = iframe.contentWindow.document.documentElement.scrollHeight + 'px';
114 if(isHidden) D.addClass(iframe, 'hidden');
115 }
116 };
117
118 cf.prototype = {
119 scrollIntoView: function(){
120 this.e.content.scrollIntoView();
121 },
122 setMessage: function(m){
123 const ds = this.e.body.dataset;
124 ds.timestamp = m.mtime;
125 ds.lmtime = m.lmtime;
126 ds.msgid = m.msgid;
127 ds.xfrom = m.xfrom || '';
128
129 if(m.uclr){
130 this.e.content.style.backgroundColor = m.uclr;
131 this.e.tab.style.backgroundColor = m.uclr;
132 }
133 const d = new Date(m.mtime);
134 D.clearElement(this.e.tab);
135 var contentTarget = this.e.content;
136 var eXFrom /* element holding xfrom name */;
137 var eXFrom = D.append(D.addClass(D.span(), 'xfrom'), m.xfrom);
138 const wrapper = D.append(
139 D.span(), eXFrom,
140 D.text(" #",(m.msgid||'???'),' @ ',d.toLocaleString()));
141 D.append(this.e.tab, wrapper);
142
143 if( m.xfrom && m.fsize>0 ){
144 if( m.fmime
145 && m.fmime.startsWith("image/")
146 /* && Chat.settings.getBool('images-inline',true) */
147 ){
148 const extension = m.fname.split('.').pop();
149 contentTarget.appendChild(D.img("chat-download/" + m.msgid +(
150 extension ? ('.'+extension) : ''/*So that IMG tag mimetype guessing works*/
151 )));
152 ds.hasImage = 1;
153 }else{
154 // Add a download link.
155 const downloadUri = window.fossil.rootPath+
156 'chat-download/' + m.msgid+'/'+encodeURIComponent(m.fname);
157 const w = D.addClass(D.div(), 'attachment-link');
158 const a = D.a(downloadUri,
159 // ^^^ add m.fname to URL to cause downloaded file to have that name.
160 "(" + m.fname + " " + m.fsize + " bytes)"
161 )
162 D.attr(a,'target','_blank');
163 D.append(w, a);
164 if(canEmbedFile(m)){
165 /* Add an option to embed HTML attachments in an iframe. The primary
166 use case is attached diffs. */
167 const shouldWikiRender = shouldWikiRenderEmbed(m);
168 const downloadArgs = shouldWikiRender ? '?render' : '';
169 D.addClass(contentTarget, 'wide');
170 const embedTarget = this.e.content;
171 const self = this;
172 const btnEmbed = D.attr(D.checkbox("1", false), 'id',
173 'embed-'+ds.msgid);
174 const btnLabel = D.label(btnEmbed, shouldWikiRender
175 ? "Embed (fossil-rendered)" : "Embed");
176 /* Maintenance reminder: do not disable the toggle
177 button while the content is loading because that will
178 cause it to get stuck in disabled mode if the browser
179 decides that loading the content should prompt the
180 user to download it, rather than embed it in the
181 iframe. */
182 btnEmbed.addEventListener('change',function(){
183 if(self.e.iframe){
184 if(btnEmbed.checked){
185 D.removeClass(self.e.iframe, 'hidden');
186 if(self.e.$iframeLoaded) adjustIFrameSize(self);
187 }
188 else D.addClass(self.e.iframe, 'hidden');
189 return;
190 }
191 const iframe = self.e.iframe = document.createElement('iframe');
192 D.append(embedTarget, iframe);
193 iframe.addEventListener('load', function(){
194 self.e.$iframeLoaded = true;
195 adjustIFrameSize(self);
196 });
197 iframe.setAttribute('src', downloadUri + downloadArgs);
198 });
199 D.append(w, btnEmbed, btnLabel);
200 }
201 contentTarget.appendChild(w);
202 }
203 }
204 if(m.xmsg){
205 if(m.fsize>0){
206 /* We have file/image content, so need another element for
207 the message text. */
208 contentTarget = D.div();
209 D.append(this.e.content, contentTarget);
210 }
211 D.addClass(contentTarget, 'content-target'
212 /*target element for the 'toggle text mode' feature*/);
213 // The m.xmsg text comes from the same server as this script and
214 // is guaranteed by that server to be "safe" HTML - safe in the
215 // sense that it is not possible for a malefactor to inject HTML
216 // or javascript or CSS. The m.xmsg content might contain
217 // hyperlinks, but otherwise it will be markup-free. See the
218 // chat_format_to_html() routine in the server for details.
219 //
220 // Hence, even though innerHTML is normally frowned upon, it is
221 // perfectly safe to use in this context.
222 if(m.xmsg && 'string' !== typeof m.xmsg){
223 // Used by Chat.reportErrorAsMessage()
224 D.append(contentTarget, m.xmsg);
225 }else{
226 contentTarget.innerHTML = m.xmsg;
227 // contentTarget.querySelectorAll('a').forEach(addAnchorTargetBlank);
228 if(F.pikchr){
229 F.pikchr.addSrcView(contentTarget.querySelectorAll('svg.pikchr'));
230 }
231 }
232 }
233 //console.debug("tab",this.e.tab);
234 //console.debug("this.e.tab.firstElementChild",this.e.tab.firstElementChild);
235 // this.e.tab.firstElementChild.addEventListener('click', this._handleLegendClicked, false);
236 /*if(eXFrom){
237 eXFrom.addEventListener('click', ()=>this.e.tab.click(), false);
238 }*/
239 return this;
240 }
241 };
242 return cf;
243 })()/*MessageWidget*/;
244
245 /************************************************************************/
246 /************************************************************************/
247 /************************************************************************/
248
249 var MessageSpacer = (function(){
250 const nMsgContext = 5;
251 const zUpArrow = '\u25B2';
252 const zDownArrow = '\u25BC';
253
254 const cf = function(o){
255
256 /* iFirstInTable:
257 ** msgid of first row in chatfts table.
258 **
259 ** iLastInTable:
260 ** msgid of last row in chatfts table.
261 **
262 ** iPrevId:
263 ** msgid of message immediately above this spacer. Or 0 if this
264 ** spacer is above all results.
265 **
266 ** iNextId:
267 ** msgid of message immediately below this spacer. Or 0 if this
268 ** spacer is below all results.
269 **
270 ** bIgnoreClick:
271 ** ignore any clicks if this is true. This is used to ensure there
272 ** is only ever one request belonging to this widget outstanding
273 ** at any time.
274 */
275 this.o = {
276 iFirstInTable: o.first,
277 iLastInTable: o.last,
278 iPrevId: o.previd,
279 iNextId: o.nextid,
280 bIgnoreClick: false,
281 };
282
283 this.e = {
284 body: D.addClass(D.div(), 'spacer-widget'),
285
286 above: D.addClass(D.div(), 'spacer-widget-above'),
287 buttons: D.addClass(D.div(), 'spacer-widget-buttons'),
288 below: D.addClass(D.div(), 'spacer-widget-below'),
289
290 up: D.button(zDownArrow+' Load '+nMsgContext+' more '+zDownArrow),
291 down: D.button(zUpArrow+' Load '+nMsgContext+' more '+zUpArrow),
292 all: D.button('Load More'),
293 };
294
295 D.addClass(this.e.up, 'up');
296 D.addClass(this.e.down, 'down');
297 D.addClass(this.e.all, 'all');
298
299 D.append(this.e.buttons, this.e.up, this.e.down, this.e.all);
300 D.append(this.e.body, this.e.above, this.e.buttons, this.e.below);
301
302 const ms = this;
303 this.e.up.addEventListener('click', function(){
304 ms.load_messages(false);
305 });
306 this.e.down.addEventListener('click', function(){
307 ms.load_messages(true);
308 });
309 this.e.all.addEventListener('click', function(){
310 ms.load_messages( (ms.o.iPrevId==0) );
311 });
312
313 this.set_button_visibility();
314 };
315
316 cf.prototype = {
317 set_button_visibility: function() {
318 var o = this.o;
319
320 var iPrevId = (o.iPrevId!=0) ? o.iPrevId : o.iFirstInTable-1;
321 var iNextId = (o.iNextId!=0) ? o.iNextId : o.iLastInTable+1;
322 var nDiff = (iNextId - iPrevId) - 1;
323
324 D.addClass([this.e.up, this.e.down, this.e.all], 'hidden');
325
326 if( nDiff>0 ){
327
328 if( nDiff>nMsgContext && (o.iPrevId==0 || o.iNextId==0) ){
329 nDiff = nMsgContext;
330 }
331
332 if( nDiff<=nMsgContext && o.iPrevId!=0 && o.iNextId!=0 ){
333 D.removeClass(this.e.all, 'hidden');
334 this.e.all.innerText = (
335 zUpArrow + " Load " + nDiff + " more " + zDownArrow
336 );
337 }else{
338 if( o.iPrevId!=0 ) D.removeClass(this.e.up, 'hidden');
339 if( o.iNextId!=0 ) D.removeClass(this.e.down, 'hidden');
340 }
341 }
342 },
343
344 load_messages: function(bDown) {
345 var iFirst = 0; /* msgid of first message to fetch */
346 var nFetch = 0; /* Number of messages to fetch */
347 var iEof = 0; /* last msgid in spacers range, plus 1 */
348
349 var e = this.e;
350 var o = this.o;
351
352 if( this.bIgnoreClick ) return;
353 this.bIgnoreClick = true;
354
355 /* Figure out the required range of messages. */
356 if( bDown ){
357 iFirst = this.o.iNextId - nMsgContext;
358 if( iFirst<this.o.iFirstInTable ){
359 iFirst = this.o.iFirstInTable;
360 }
361 }else{
362 iFirst = this.o.iPrevId+1;
363 }
364 nFetch = nMsgContext;
365 iEof = (this.o.iNextId > 0) ? this.o.iNextId : this.o.iLastInTable+1;
366 if( iFirst+nFetch>iEof ){
367 nFetch = iEof - iFirst;
368 }
369
370
371 const ms = this;
372 F.fetch("chat-query",{
373 urlParams:{
374 q: '',
375 n: nFetch,
376 i: iFirst
377 },
378 responseType: "json",
379
380 onerror:function(err){
381 console.error(err);
382 alert(err.toString());
383 },
384
385 onload:function(jx){
386 const firstChildOfBelow = e.below.firstChild;
387 jx.msgs.forEach((m) => {
388 var mw = new MessageWidget(m);
389 if( bDown ){
390 e.below.insertBefore(mw.e.body, firstChildOfBelow);
391 }else{
392 D.append(e.above, mw.e.body);
393 }
394 });
395
396 if( bDown ){
397 o.iNextId -= jx.msgs.length;
398 }else{
399 o.iPrevId += jx.msgs.length;
400 }
401
402 ms.set_button_visibility();
403 ms.bIgnoreClick = false;
404 }
405 });
406 }
407 };
408
409 return cf;
410 })(); /* MessageSpacer */
411
412 /* This is called to submit a search - because the user clicked the
413 ** search button or pressed Enter in the input box.
414 */
415 const submit_search = function() {
416 const v = E1('#textinput').value;
417 F.fetch("chat-query",{
418 urlParams:{
419 q: v
420 },
421 responseType: "json",
422
423 onerror:function(err){
424 console.error(err);
425 alert(err.toString());
426 },
427
428 onload:function(jx){
429 var res = E1('#results');
430 var previd = 0;
431
432 D.clearElement(res);
433 jx.msgs.forEach((m) => {
434 var mw = new MessageWidget(m);
435 var spacer = new MessageSpacer({
436 first: jx.first,
437 last: jx.last,
438 previd: previd,
439 nextid: m.msgid
440 });
441
442 D.append( res, spacer.e.body );
443 D.append( res, mw.e.body );
444
445 previd = m.msgid;
446 });
447
448 if( jx.msgs.length>0 ){
449 var spacer = new MessageSpacer({
450 first: jx.first,
451 last: jx.last,
452 previd: previd,
453 nextid: 0
454 });
455 D.append( res, spacer.e.body );
456 } else {
457 res.innerHTML = '<center><i>No query results</i></center>';
458 }
459
460 window.scrollTo(0, E1('body').scrollHeight);
461 }
462 });
463 }
464
465 /* Add event listeners to call submit_search() if the user presses Enter
466 ** or clicks the search button.
467 */
468 E1('#searchbutton').addEventListener('click', function(){
469 submit_search();
470 });
471 E1('#textinput').addEventListener('keydown', function(ev){
472 if( 13==ev.keyCode ){
473 /* If the key pressed was Enter */
474 submit_search();
475 }
476 });
477
478 /* Focus the input widget */
479 E1('#textinput').focus();
480
481 });
--- a/src/fossil.page.chatsearch.js
+++ b/src/fossil.page.chatsearch.js
@@ -1,481 +0,0 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- src/main.mk
+++ src/main.mk
@@ -231,11 +231,10 @@
231231
$(SRCDIR)/fossil.dom.js \
232232
$(SRCDIR)/fossil.fetch.js \
233233
$(SRCDIR)/fossil.numbered-lines.js \
234234
$(SRCDIR)/fossil.page.brlist.js \
235235
$(SRCDIR)/fossil.page.chat.js \
236
- $(SRCDIR)/fossil.page.chatsearch.js \
237236
$(SRCDIR)/fossil.page.fileedit.js \
238237
$(SRCDIR)/fossil.page.forumpost.js \
239238
$(SRCDIR)/fossil.page.pikchrshow.js \
240239
$(SRCDIR)/fossil.page.pikchrshowasm.js \
241240
$(SRCDIR)/fossil.page.whistory.js \
242241
--- src/main.mk
+++ src/main.mk
@@ -231,11 +231,10 @@
231 $(SRCDIR)/fossil.dom.js \
232 $(SRCDIR)/fossil.fetch.js \
233 $(SRCDIR)/fossil.numbered-lines.js \
234 $(SRCDIR)/fossil.page.brlist.js \
235 $(SRCDIR)/fossil.page.chat.js \
236 $(SRCDIR)/fossil.page.chatsearch.js \
237 $(SRCDIR)/fossil.page.fileedit.js \
238 $(SRCDIR)/fossil.page.forumpost.js \
239 $(SRCDIR)/fossil.page.pikchrshow.js \
240 $(SRCDIR)/fossil.page.pikchrshowasm.js \
241 $(SRCDIR)/fossil.page.whistory.js \
242
--- src/main.mk
+++ src/main.mk
@@ -231,11 +231,10 @@
231 $(SRCDIR)/fossil.dom.js \
232 $(SRCDIR)/fossil.fetch.js \
233 $(SRCDIR)/fossil.numbered-lines.js \
234 $(SRCDIR)/fossil.page.brlist.js \
235 $(SRCDIR)/fossil.page.chat.js \
 
236 $(SRCDIR)/fossil.page.fileedit.js \
237 $(SRCDIR)/fossil.page.forumpost.js \
238 $(SRCDIR)/fossil.page.pikchrshow.js \
239 $(SRCDIR)/fossil.page.pikchrshowasm.js \
240 $(SRCDIR)/fossil.page.whistory.js \
241
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -617,11 +617,10 @@
617617
$(SRCDIR)/fossil.dom.js \
618618
$(SRCDIR)/fossil.fetch.js \
619619
$(SRCDIR)/fossil.numbered-lines.js \
620620
$(SRCDIR)/fossil.page.brlist.js \
621621
$(SRCDIR)/fossil.page.chat.js \
622
- $(SRCDIR)/fossil.page.chatsearch.js \
623622
$(SRCDIR)/fossil.page.fileedit.js \
624623
$(SRCDIR)/fossil.page.forumpost.js \
625624
$(SRCDIR)/fossil.page.pikchrshow.js \
626625
$(SRCDIR)/fossil.page.pikchrshowasm.js \
627626
$(SRCDIR)/fossil.page.whistory.js \
628627
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -617,11 +617,10 @@
617 $(SRCDIR)/fossil.dom.js \
618 $(SRCDIR)/fossil.fetch.js \
619 $(SRCDIR)/fossil.numbered-lines.js \
620 $(SRCDIR)/fossil.page.brlist.js \
621 $(SRCDIR)/fossil.page.chat.js \
622 $(SRCDIR)/fossil.page.chatsearch.js \
623 $(SRCDIR)/fossil.page.fileedit.js \
624 $(SRCDIR)/fossil.page.forumpost.js \
625 $(SRCDIR)/fossil.page.pikchrshow.js \
626 $(SRCDIR)/fossil.page.pikchrshowasm.js \
627 $(SRCDIR)/fossil.page.whistory.js \
628
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -617,11 +617,10 @@
617 $(SRCDIR)/fossil.dom.js \
618 $(SRCDIR)/fossil.fetch.js \
619 $(SRCDIR)/fossil.numbered-lines.js \
620 $(SRCDIR)/fossil.page.brlist.js \
621 $(SRCDIR)/fossil.page.chat.js \
 
622 $(SRCDIR)/fossil.page.fileedit.js \
623 $(SRCDIR)/fossil.page.forumpost.js \
624 $(SRCDIR)/fossil.page.pikchrshow.js \
625 $(SRCDIR)/fossil.page.pikchrshowasm.js \
626 $(SRCDIR)/fossil.page.whistory.js \
627
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -575,11 +575,10 @@
575575
"$(SRCDIR)\fossil.dom.js" \
576576
"$(SRCDIR)\fossil.fetch.js" \
577577
"$(SRCDIR)\fossil.numbered-lines.js" \
578578
"$(SRCDIR)\fossil.page.brlist.js" \
579579
"$(SRCDIR)\fossil.page.chat.js" \
580
- "$(SRCDIR)\fossil.page.chatsearch.js" \
581580
"$(SRCDIR)\fossil.page.fileedit.js" \
582581
"$(SRCDIR)\fossil.page.forumpost.js" \
583582
"$(SRCDIR)\fossil.page.pikchrshow.js" \
584583
"$(SRCDIR)\fossil.page.pikchrshowasm.js" \
585584
"$(SRCDIR)\fossil.page.whistory.js" \
@@ -1205,11 +1204,10 @@
12051204
echo "$(SRCDIR)\fossil.dom.js" >> $@
12061205
echo "$(SRCDIR)\fossil.fetch.js" >> $@
12071206
echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@
12081207
echo "$(SRCDIR)\fossil.page.brlist.js" >> $@
12091208
echo "$(SRCDIR)\fossil.page.chat.js" >> $@
1210
- echo "$(SRCDIR)\fossil.page.chatsearch.js" >> $@
12111209
echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@
12121210
echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@
12131211
echo "$(SRCDIR)\fossil.page.pikchrshow.js" >> $@
12141212
echo "$(SRCDIR)\fossil.page.pikchrshowasm.js" >> $@
12151213
echo "$(SRCDIR)\fossil.page.whistory.js" >> $@
12161214
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -575,11 +575,10 @@
575 "$(SRCDIR)\fossil.dom.js" \
576 "$(SRCDIR)\fossil.fetch.js" \
577 "$(SRCDIR)\fossil.numbered-lines.js" \
578 "$(SRCDIR)\fossil.page.brlist.js" \
579 "$(SRCDIR)\fossil.page.chat.js" \
580 "$(SRCDIR)\fossil.page.chatsearch.js" \
581 "$(SRCDIR)\fossil.page.fileedit.js" \
582 "$(SRCDIR)\fossil.page.forumpost.js" \
583 "$(SRCDIR)\fossil.page.pikchrshow.js" \
584 "$(SRCDIR)\fossil.page.pikchrshowasm.js" \
585 "$(SRCDIR)\fossil.page.whistory.js" \
@@ -1205,11 +1204,10 @@
1205 echo "$(SRCDIR)\fossil.dom.js" >> $@
1206 echo "$(SRCDIR)\fossil.fetch.js" >> $@
1207 echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@
1208 echo "$(SRCDIR)\fossil.page.brlist.js" >> $@
1209 echo "$(SRCDIR)\fossil.page.chat.js" >> $@
1210 echo "$(SRCDIR)\fossil.page.chatsearch.js" >> $@
1211 echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@
1212 echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@
1213 echo "$(SRCDIR)\fossil.page.pikchrshow.js" >> $@
1214 echo "$(SRCDIR)\fossil.page.pikchrshowasm.js" >> $@
1215 echo "$(SRCDIR)\fossil.page.whistory.js" >> $@
1216
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -575,11 +575,10 @@
575 "$(SRCDIR)\fossil.dom.js" \
576 "$(SRCDIR)\fossil.fetch.js" \
577 "$(SRCDIR)\fossil.numbered-lines.js" \
578 "$(SRCDIR)\fossil.page.brlist.js" \
579 "$(SRCDIR)\fossil.page.chat.js" \
 
580 "$(SRCDIR)\fossil.page.fileedit.js" \
581 "$(SRCDIR)\fossil.page.forumpost.js" \
582 "$(SRCDIR)\fossil.page.pikchrshow.js" \
583 "$(SRCDIR)\fossil.page.pikchrshowasm.js" \
584 "$(SRCDIR)\fossil.page.whistory.js" \
@@ -1205,11 +1204,10 @@
1204 echo "$(SRCDIR)\fossil.dom.js" >> $@
1205 echo "$(SRCDIR)\fossil.fetch.js" >> $@
1206 echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@
1207 echo "$(SRCDIR)\fossil.page.brlist.js" >> $@
1208 echo "$(SRCDIR)\fossil.page.chat.js" >> $@
 
1209 echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@
1210 echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@
1211 echo "$(SRCDIR)\fossil.page.pikchrshow.js" >> $@
1212 echo "$(SRCDIR)\fossil.page.pikchrshowasm.js" >> $@
1213 echo "$(SRCDIR)\fossil.page.whistory.js" >> $@
1214

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button