Fossil SCM

Do not use <fieldset> and <legend> as Safari does not allow one to bind 'click' events.

drh 2020-12-25 23:02 trunk merge
Commit 6849bb0b6b5ae9ea1315fa88d43adb53c36be85fa7bc36cdffc383b786dc9fad
2 files changed +102 -85 +16 -16
+102 -85
--- src/chat.js
+++ src/chat.js
@@ -31,15 +31,10 @@
3131
changesSincePageHidden: 0,
3232
notificationBubbleColor: 'white',
3333
totalMessageCount: 0, // total # of inbound messages
3434
//! Number of messages to load for the history buttons
3535
loadMessageCount: Math.abs(F.config.chat.initSize || 20),
36
- /* Alignment of 'my' messages: must be 'left' or 'right'. Note
37
- that 'right' is conventional for mobile chat apps but can be
38
- difficult to read in wide windows (desktop/tablet landscape
39
- mode). Can be toggled via settings popup. */
40
- msgMyAlign: (window.innerWidth<window.innerHeight) ? 'right' : 'left',
4136
ajaxInflight: 0,
4237
/** Gets (no args) or sets (1 arg) the current input text field value,
4338
taking into account single- vs multi-line input. The getter returns
4439
a string and the setter returns this object. */
4540
inputValue: function(){
@@ -138,10 +133,17 @@
138133
};
139134
Object.keys(cs.settings.defaults).forEach(function f(k){
140135
const v = cs.settings.get(k,f);
141136
if(f===v) cs.settings.set(k,cs.settings.defaults[k]);
142137
});
138
+ if(window.innerWidth<window.innerHeight){
139
+ /* Alignment of 'my' messages: right alignment is conventional
140
+ for mobile chat apps but can be difficult to read in wide
141
+ windows (desktop/tablet landscape mode). Can be toggled via
142
+ settings popup. */
143
+ document.body.classList.add('my-messages-right');
144
+ }
143145
if(cs.settings.getBool('monospace-messages',false)){
144146
document.body.classList.add('monospace-messages');
145147
}
146148
cs.e.inputCurrent = cs.e.inputSingle;
147149
cs.pageTitleOrig = cs.e.pageTitle.innerText;
@@ -223,10 +225,93 @@
223225
}
224226
}, true);
225227
return cs;
226228
})()/*Chat initialization*/;
227229
230
+ /**
231
+ Custom widget type for rendering messages (one message per
232
+ instance). These are modelled after FIELDSET elements but we
233
+ don't use FIELDSET because of cross-browser inconsistencies in
234
+ features of the FIELDSET/LEGEND combination, e.g. inability to
235
+ align legends via CSS in Firefox and clicking-related
236
+ deficiencies in Safari.
237
+ */
238
+ const MessageWidget = (function(){
239
+ const cf = function(){
240
+ this.e = {
241
+ body: D.addClass(D.div(), 'message-widget'),
242
+ tab: D.addClass(D.span(), 'message-widget-tab'),
243
+ content: D.addClass(D.div(), 'message-widget-content')
244
+ };
245
+ D.append(this.e.body, this.e.tab);
246
+ D.append(this.e.body, this.e.content);
247
+ this.e.tab.setAttribute('role', 'button');
248
+ };
249
+ cf.prototype = {
250
+ setLabel: function(label){
251
+ return this;
252
+ },
253
+ setPopupCallback: function(callback){
254
+ this.e.tab.addEventListener('click', callback, false);
255
+ return this;
256
+ },
257
+ setMessage: function(m){
258
+ const ds = this.e.body.dataset;
259
+ ds.timestamp = m.mtime;
260
+ ds.msgid = m.msgid;
261
+ ds.xfrom = m.xfrom;
262
+ if(m.xfrom === Chat.me){
263
+ D.addClass(this.e.body, 'mine');
264
+ }
265
+ this.e.content.style.backgroundColor = m.uclr;
266
+ this.e.tab.style.backgroundColor = m.uclr;
267
+
268
+ const d = new Date(m.mtime);
269
+ D.append(
270
+ D.clearElement(this.e.tab), D.text(
271
+ m.xfrom+' @ '+d.getHours()+":"+(d.getMinutes()+100).toString().slice(1,3))
272
+ );
273
+ var contentTarget = this.e.content;
274
+ if( m.fsize>0 ){
275
+ if( m.fmime
276
+ && m.fmime.startsWith("image/")
277
+ && Chat.settings.getBool('images-inline',true)
278
+ ){
279
+ contentTarget.appendChild(D.img("chat-download/" + m.msgid));
280
+ }else{
281
+ const a = D.a(
282
+ window.fossil.rootPath+
283
+ 'chat-download/' + m.msgid+'/'+encodeURIComponent(m.fname),
284
+ // ^^^ add m.fname to URL to cause downloaded file to have that name.
285
+ "(" + m.fname + " " + m.fsize + " bytes)"
286
+ )
287
+ D.attr(a,'target','_blank');
288
+ contentTarget.appendChild(a);
289
+ }
290
+ contentTarget = D.div();
291
+ }
292
+ if(m.xmsg){
293
+ if(contentTarget !== this.e.content){
294
+ D.append(this.e.content, contentTarget);
295
+ }
296
+ // The m.xmsg text comes from the same server as this script and
297
+ // is guaranteed by that server to be "safe" HTML - safe in the
298
+ // sense that it is not possible for a malefactor to inject HTML
299
+ // or javascript or CSS. The m.xmsg content might contain
300
+ // hyperlinks, but otherwise it will be markup-free. See the
301
+ // chat_format_to_html() routine in the server for details.
302
+ //
303
+ // Hence, even though innerHTML is normally frowned upon, it is
304
+ // perfectly safe to use in this context.
305
+ contentTarget.innerHTML = m.xmsg;
306
+ }
307
+ return this;
308
+ }
309
+ };
310
+ return cf;
311
+ })()/*MessageWidget*/;
312
+
228313
const BlobXferState = (function(){/*drag/drop bits...*/
229314
/* State for paste and drag/drop */
230315
const bxs = {
231316
dropDetails: document.querySelector('#chat-drop-details'),
232317
blob: undefined,
@@ -413,19 +498,21 @@
413498
D.clearElement(this.e);
414499
return this.show(false);
415500
};
416501
}/*end static init*/
417502
const rect = ev.target.getBoundingClientRect();
418
- const eMsg = ev.target.parentNode/*the owning fieldset element*/;
503
+ const eMsg = ev.target.parentNode/*the owning .message-widget element*/;
419504
f.popup._eMsg = eMsg;
420
- let x = rect.left, y = rect.top - 10;
505
+ console.debug("eMsg, rect", eMsg, rect);
506
+ let x = rect.left, y = rect.topm;
421507
f.popup.show(ev.target)/*so we can get its computed size*/;
422
- if('right'===ev.target.getAttribute('align')){
508
+ if(eMsg.dataset.xfrom===Chat.me
509
+ && document.body.classList.contains('my-messages-right')){
423510
// Shift popup to the left for right-aligned messages to avoid
424511
// truncation off the right edge of the page.
425512
const pRect = f.popup.e.getBoundingClientRect();
426
- x -= pRect.width/3*2;
513
+ x = rect.right - pRect.width;
427514
}
428515
f.popup.show(x, y);
429516
}/*handleLegendClicked()*/;
430517
431518
(function(){/*Set up #chat-settings-button */
@@ -495,21 +582,13 @@
495582
iws.backgroundColor = f.initialBg;
496583
}
497584
}
498585
},{
499586
label: "Left-align my posts",
500
- boolValue: ()=>'left'===Chat.msgMyAlign,
587
+ boolValue: ()=>!document.body.classList.contains('my-messages-right'),
501588
callback: function f(){
502
- if('right'===Chat.msgMyAlign) Chat.msgMyAlign = 'left';
503
- else Chat.msgMyAlign = 'right';
504
- const msgs = Chat.e.messagesWrapper.querySelectorAll('.message-row');
505
- msgs.forEach(function(row){
506
- if(row.dataset.xfrom!==Chat.me) return;
507
- row.querySelector('legend').setAttribute('align', Chat.msgMyAlign);
508
- if('right'===Chat.msgMyAlign) row.style.justifyContent = "flex-end";
509
- else row.style.justifyContent = "flex-start";
510
- });
589
+ document.body.classList.toggle('my-messages-right');
511590
}
512591
},{
513592
label: "Images inline",
514593
boolValue: ()=>Chat.settings.getBool('images-inline'),
515594
callback: function(){
@@ -586,76 +665,14 @@
586665
if( m.mdel ){
587666
/* A record deletion notice. */
588667
Chat.deleteMessageElem(m.mdel);
589668
return;
590669
}
591
- const eWho = D.create('legend'),
592
- row = D.addClass(D.fieldset(eWho), 'message-row');
593
- row.dataset.msgid = m.msgid;
594
- row.dataset.xfrom = m.xfrom;
595
- row.dataset.timestamp = m.mtime;
596
- Chat.injectMessageElem(row,atEnd);
597
- eWho.addEventListener('click', handleLegendClicked, false);
598
- eWho.setAttribute('role', 'button');
599
- if( m.xfrom==Chat.me ){
600
- eWho.setAttribute('align', Chat.msgMyAlign);
601
- if('right'===Chat.msgMyAlign){
602
- row.style.justifyContent = "flex-end";
603
- }else{
604
- row.style.justifyContent = "flex-start";
605
- }
606
- }else{
607
- eWho.setAttribute('align', 'left');
608
- }
609
- eWho.style.backgroundColor = m.uclr;
610
- eWho.classList.add('message-user');
611
- let whoName = m.xfrom;
612
- var d = new Date(m.mtime);
613
- if( d.getMinutes().toString()!="NaN" ){
614
- /* Show local time when we can compute it */
615
- eWho.append(D.text(whoName+' @ '+
616
- d.getHours()+":"+(d.getMinutes()+100).toString().slice(1,3)
617
- ))
618
- }else{
619
- /* Show UTC on systems where Date() does not work */
620
- eWho.append(D.text(whoName+' @ '+m.mtime.slice(11,16)))
621
- }
622
- let eContent = D.addClass(D.div(),'message-content','chat-message');
623
- eContent.style.backgroundColor = m.uclr;
624
- row.appendChild(eContent);
625
- if( m.fsize>0 ){
626
- if( m.fmime
627
- && m.fmime.startsWith("image/")
628
- && Chat.settings.getBool('images-inline',true)
629
- ){
630
- eContent.appendChild(D.img("chat-download/" + m.msgid));
631
- }else{
632
- const a = D.a(
633
- window.fossil.rootPath+
634
- 'chat-download/' + m.msgid+'/'+encodeURIComponent(m.fname),
635
- // ^^^ add m.fname to URL to cause downloaded file to have that name.
636
- "(" + m.fname + " " + m.fsize + " bytes)"
637
- )
638
- D.attr(a,'target','_blank');
639
- eContent.appendChild(a);
640
- }
641
- const br = D.br();
642
- br.style.clear = "both";
643
- eContent.appendChild(br);
644
- }
645
- if(m.xmsg){
646
- // The m.xmsg text comes from the same server as this script and
647
- // is guaranteed by that server to be "safe" HTML - safe in the
648
- // sense that it is not possible for a malefactor to inject HTML
649
- // or javascript or CSS. The m.xmsg content might contain
650
- // hyperlinks, but otherwise it will be markup-free. See the
651
- // chat_format_to_html() routine in the server for details.
652
- //
653
- // Hence, even though innerHTML is normally frowned upon, it is
654
- // perfectly safe to use in this context.
655
- eContent.innerHTML += m.xmsg
656
- }
670
+ const row = new MessageWidget()
671
+ row.setMessage(m);
672
+ row.setPopupCallback(handleLegendClicked);
673
+ Chat.injectMessageElem(row.e.body,atEnd);
657674
}/*processPost()*/;
658675
}/*end static init*/
659676
jx.msgs.forEach((m)=>f.processPost(m,atEnd));
660677
if('visible'===document.visibilityState){
661678
if(Chat.changesSincePageHidden){
662679
--- src/chat.js
+++ src/chat.js
@@ -31,15 +31,10 @@
31 changesSincePageHidden: 0,
32 notificationBubbleColor: 'white',
33 totalMessageCount: 0, // total # of inbound messages
34 //! Number of messages to load for the history buttons
35 loadMessageCount: Math.abs(F.config.chat.initSize || 20),
36 /* Alignment of 'my' messages: must be 'left' or 'right'. Note
37 that 'right' is conventional for mobile chat apps but can be
38 difficult to read in wide windows (desktop/tablet landscape
39 mode). Can be toggled via settings popup. */
40 msgMyAlign: (window.innerWidth<window.innerHeight) ? 'right' : 'left',
41 ajaxInflight: 0,
42 /** Gets (no args) or sets (1 arg) the current input text field value,
43 taking into account single- vs multi-line input. The getter returns
44 a string and the setter returns this object. */
45 inputValue: function(){
@@ -138,10 +133,17 @@
138 };
139 Object.keys(cs.settings.defaults).forEach(function f(k){
140 const v = cs.settings.get(k,f);
141 if(f===v) cs.settings.set(k,cs.settings.defaults[k]);
142 });
 
 
 
 
 
 
 
143 if(cs.settings.getBool('monospace-messages',false)){
144 document.body.classList.add('monospace-messages');
145 }
146 cs.e.inputCurrent = cs.e.inputSingle;
147 cs.pageTitleOrig = cs.e.pageTitle.innerText;
@@ -223,10 +225,93 @@
223 }
224 }, true);
225 return cs;
226 })()/*Chat initialization*/;
227
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228 const BlobXferState = (function(){/*drag/drop bits...*/
229 /* State for paste and drag/drop */
230 const bxs = {
231 dropDetails: document.querySelector('#chat-drop-details'),
232 blob: undefined,
@@ -413,19 +498,21 @@
413 D.clearElement(this.e);
414 return this.show(false);
415 };
416 }/*end static init*/
417 const rect = ev.target.getBoundingClientRect();
418 const eMsg = ev.target.parentNode/*the owning fieldset element*/;
419 f.popup._eMsg = eMsg;
420 let x = rect.left, y = rect.top - 10;
 
421 f.popup.show(ev.target)/*so we can get its computed size*/;
422 if('right'===ev.target.getAttribute('align')){
 
423 // Shift popup to the left for right-aligned messages to avoid
424 // truncation off the right edge of the page.
425 const pRect = f.popup.e.getBoundingClientRect();
426 x -= pRect.width/3*2;
427 }
428 f.popup.show(x, y);
429 }/*handleLegendClicked()*/;
430
431 (function(){/*Set up #chat-settings-button */
@@ -495,21 +582,13 @@
495 iws.backgroundColor = f.initialBg;
496 }
497 }
498 },{
499 label: "Left-align my posts",
500 boolValue: ()=>'left'===Chat.msgMyAlign,
501 callback: function f(){
502 if('right'===Chat.msgMyAlign) Chat.msgMyAlign = 'left';
503 else Chat.msgMyAlign = 'right';
504 const msgs = Chat.e.messagesWrapper.querySelectorAll('.message-row');
505 msgs.forEach(function(row){
506 if(row.dataset.xfrom!==Chat.me) return;
507 row.querySelector('legend').setAttribute('align', Chat.msgMyAlign);
508 if('right'===Chat.msgMyAlign) row.style.justifyContent = "flex-end";
509 else row.style.justifyContent = "flex-start";
510 });
511 }
512 },{
513 label: "Images inline",
514 boolValue: ()=>Chat.settings.getBool('images-inline'),
515 callback: function(){
@@ -586,76 +665,14 @@
586 if( m.mdel ){
587 /* A record deletion notice. */
588 Chat.deleteMessageElem(m.mdel);
589 return;
590 }
591 const eWho = D.create('legend'),
592 row = D.addClass(D.fieldset(eWho), 'message-row');
593 row.dataset.msgid = m.msgid;
594 row.dataset.xfrom = m.xfrom;
595 row.dataset.timestamp = m.mtime;
596 Chat.injectMessageElem(row,atEnd);
597 eWho.addEventListener('click', handleLegendClicked, false);
598 eWho.setAttribute('role', 'button');
599 if( m.xfrom==Chat.me ){
600 eWho.setAttribute('align', Chat.msgMyAlign);
601 if('right'===Chat.msgMyAlign){
602 row.style.justifyContent = "flex-end";
603 }else{
604 row.style.justifyContent = "flex-start";
605 }
606 }else{
607 eWho.setAttribute('align', 'left');
608 }
609 eWho.style.backgroundColor = m.uclr;
610 eWho.classList.add('message-user');
611 let whoName = m.xfrom;
612 var d = new Date(m.mtime);
613 if( d.getMinutes().toString()!="NaN" ){
614 /* Show local time when we can compute it */
615 eWho.append(D.text(whoName+' @ '+
616 d.getHours()+":"+(d.getMinutes()+100).toString().slice(1,3)
617 ))
618 }else{
619 /* Show UTC on systems where Date() does not work */
620 eWho.append(D.text(whoName+' @ '+m.mtime.slice(11,16)))
621 }
622 let eContent = D.addClass(D.div(),'message-content','chat-message');
623 eContent.style.backgroundColor = m.uclr;
624 row.appendChild(eContent);
625 if( m.fsize>0 ){
626 if( m.fmime
627 && m.fmime.startsWith("image/")
628 && Chat.settings.getBool('images-inline',true)
629 ){
630 eContent.appendChild(D.img("chat-download/" + m.msgid));
631 }else{
632 const a = D.a(
633 window.fossil.rootPath+
634 'chat-download/' + m.msgid+'/'+encodeURIComponent(m.fname),
635 // ^^^ add m.fname to URL to cause downloaded file to have that name.
636 "(" + m.fname + " " + m.fsize + " bytes)"
637 )
638 D.attr(a,'target','_blank');
639 eContent.appendChild(a);
640 }
641 const br = D.br();
642 br.style.clear = "both";
643 eContent.appendChild(br);
644 }
645 if(m.xmsg){
646 // The m.xmsg text comes from the same server as this script and
647 // is guaranteed by that server to be "safe" HTML - safe in the
648 // sense that it is not possible for a malefactor to inject HTML
649 // or javascript or CSS. The m.xmsg content might contain
650 // hyperlinks, but otherwise it will be markup-free. See the
651 // chat_format_to_html() routine in the server for details.
652 //
653 // Hence, even though innerHTML is normally frowned upon, it is
654 // perfectly safe to use in this context.
655 eContent.innerHTML += m.xmsg
656 }
657 }/*processPost()*/;
658 }/*end static init*/
659 jx.msgs.forEach((m)=>f.processPost(m,atEnd));
660 if('visible'===document.visibilityState){
661 if(Chat.changesSincePageHidden){
662
--- src/chat.js
+++ src/chat.js
@@ -31,15 +31,10 @@
31 changesSincePageHidden: 0,
32 notificationBubbleColor: 'white',
33 totalMessageCount: 0, // total # of inbound messages
34 //! Number of messages to load for the history buttons
35 loadMessageCount: Math.abs(F.config.chat.initSize || 20),
 
 
 
 
 
36 ajaxInflight: 0,
37 /** Gets (no args) or sets (1 arg) the current input text field value,
38 taking into account single- vs multi-line input. The getter returns
39 a string and the setter returns this object. */
40 inputValue: function(){
@@ -138,10 +133,17 @@
133 };
134 Object.keys(cs.settings.defaults).forEach(function f(k){
135 const v = cs.settings.get(k,f);
136 if(f===v) cs.settings.set(k,cs.settings.defaults[k]);
137 });
138 if(window.innerWidth<window.innerHeight){
139 /* Alignment of 'my' messages: right alignment is conventional
140 for mobile chat apps but can be difficult to read in wide
141 windows (desktop/tablet landscape mode). Can be toggled via
142 settings popup. */
143 document.body.classList.add('my-messages-right');
144 }
145 if(cs.settings.getBool('monospace-messages',false)){
146 document.body.classList.add('monospace-messages');
147 }
148 cs.e.inputCurrent = cs.e.inputSingle;
149 cs.pageTitleOrig = cs.e.pageTitle.innerText;
@@ -223,10 +225,93 @@
225 }
226 }, true);
227 return cs;
228 })()/*Chat initialization*/;
229
230 /**
231 Custom widget type for rendering messages (one message per
232 instance). These are modelled after FIELDSET elements but we
233 don't use FIELDSET because of cross-browser inconsistencies in
234 features of the FIELDSET/LEGEND combination, e.g. inability to
235 align legends via CSS in Firefox and clicking-related
236 deficiencies in Safari.
237 */
238 const MessageWidget = (function(){
239 const cf = function(){
240 this.e = {
241 body: D.addClass(D.div(), 'message-widget'),
242 tab: D.addClass(D.span(), 'message-widget-tab'),
243 content: D.addClass(D.div(), 'message-widget-content')
244 };
245 D.append(this.e.body, this.e.tab);
246 D.append(this.e.body, this.e.content);
247 this.e.tab.setAttribute('role', 'button');
248 };
249 cf.prototype = {
250 setLabel: function(label){
251 return this;
252 },
253 setPopupCallback: function(callback){
254 this.e.tab.addEventListener('click', callback, false);
255 return this;
256 },
257 setMessage: function(m){
258 const ds = this.e.body.dataset;
259 ds.timestamp = m.mtime;
260 ds.msgid = m.msgid;
261 ds.xfrom = m.xfrom;
262 if(m.xfrom === Chat.me){
263 D.addClass(this.e.body, 'mine');
264 }
265 this.e.content.style.backgroundColor = m.uclr;
266 this.e.tab.style.backgroundColor = m.uclr;
267
268 const d = new Date(m.mtime);
269 D.append(
270 D.clearElement(this.e.tab), D.text(
271 m.xfrom+' @ '+d.getHours()+":"+(d.getMinutes()+100).toString().slice(1,3))
272 );
273 var contentTarget = this.e.content;
274 if( m.fsize>0 ){
275 if( m.fmime
276 && m.fmime.startsWith("image/")
277 && Chat.settings.getBool('images-inline',true)
278 ){
279 contentTarget.appendChild(D.img("chat-download/" + m.msgid));
280 }else{
281 const a = D.a(
282 window.fossil.rootPath+
283 'chat-download/' + m.msgid+'/'+encodeURIComponent(m.fname),
284 // ^^^ add m.fname to URL to cause downloaded file to have that name.
285 "(" + m.fname + " " + m.fsize + " bytes)"
286 )
287 D.attr(a,'target','_blank');
288 contentTarget.appendChild(a);
289 }
290 contentTarget = D.div();
291 }
292 if(m.xmsg){
293 if(contentTarget !== this.e.content){
294 D.append(this.e.content, contentTarget);
295 }
296 // The m.xmsg text comes from the same server as this script and
297 // is guaranteed by that server to be "safe" HTML - safe in the
298 // sense that it is not possible for a malefactor to inject HTML
299 // or javascript or CSS. The m.xmsg content might contain
300 // hyperlinks, but otherwise it will be markup-free. See the
301 // chat_format_to_html() routine in the server for details.
302 //
303 // Hence, even though innerHTML is normally frowned upon, it is
304 // perfectly safe to use in this context.
305 contentTarget.innerHTML = m.xmsg;
306 }
307 return this;
308 }
309 };
310 return cf;
311 })()/*MessageWidget*/;
312
313 const BlobXferState = (function(){/*drag/drop bits...*/
314 /* State for paste and drag/drop */
315 const bxs = {
316 dropDetails: document.querySelector('#chat-drop-details'),
317 blob: undefined,
@@ -413,19 +498,21 @@
498 D.clearElement(this.e);
499 return this.show(false);
500 };
501 }/*end static init*/
502 const rect = ev.target.getBoundingClientRect();
503 const eMsg = ev.target.parentNode/*the owning .message-widget element*/;
504 f.popup._eMsg = eMsg;
505 console.debug("eMsg, rect", eMsg, rect);
506 let x = rect.left, y = rect.topm;
507 f.popup.show(ev.target)/*so we can get its computed size*/;
508 if(eMsg.dataset.xfrom===Chat.me
509 && document.body.classList.contains('my-messages-right')){
510 // Shift popup to the left for right-aligned messages to avoid
511 // truncation off the right edge of the page.
512 const pRect = f.popup.e.getBoundingClientRect();
513 x = rect.right - pRect.width;
514 }
515 f.popup.show(x, y);
516 }/*handleLegendClicked()*/;
517
518 (function(){/*Set up #chat-settings-button */
@@ -495,21 +582,13 @@
582 iws.backgroundColor = f.initialBg;
583 }
584 }
585 },{
586 label: "Left-align my posts",
587 boolValue: ()=>!document.body.classList.contains('my-messages-right'),
588 callback: function f(){
589 document.body.classList.toggle('my-messages-right');
 
 
 
 
 
 
 
 
590 }
591 },{
592 label: "Images inline",
593 boolValue: ()=>Chat.settings.getBool('images-inline'),
594 callback: function(){
@@ -586,76 +665,14 @@
665 if( m.mdel ){
666 /* A record deletion notice. */
667 Chat.deleteMessageElem(m.mdel);
668 return;
669 }
670 const row = new MessageWidget()
671 row.setMessage(m);
672 row.setPopupCallback(handleLegendClicked);
673 Chat.injectMessageElem(row.e.body,atEnd);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
674 }/*processPost()*/;
675 }/*end static init*/
676 jx.msgs.forEach((m)=>f.processPost(m,atEnd));
677 if('visible'===document.visibilityState){
678 if(Chat.changesSincePageHidden){
679
+16 -16
--- src/default.css
+++ src/default.css
@@ -1473,45 +1473,45 @@
14731473
body.chat span.at-name { /* for @USERNAME references */
14741474
text-decoration: underline;
14751475
font-weight: bold;
14761476
}
14771477
/* A wrapper for a single single message (one row of the UI) */
1478
-body.chat .message-row {
1479
- margin-bottom: 0.5em;
1480
- border: none;
1481
- display: flex;
1482
- flex-direction: row;
1483
- justify-content: flex-start;
1484
- /*border: 1px solid rgba(0,0,0,0.2);
1485
- border-radius: 0.25em;
1486
- box-shadow: 0.2em 0.2em 0.2em rgba(0, 0, 0, 0.29);*/
1487
- border: none;
1478
+body.chat .message-widget {
1479
+ margin-bottom: 0.75em;
1480
+ border: none;
1481
+ display: flex;
1482
+ flex-direction: column;
1483
+ border: none;
1484
+ align-items: flex-start;
1485
+}
1486
+body.chat.my-messages-right .message-widget.mine {
1487
+ align-items: flex-end;
14881488
}
14891489
/* The content area of a message (the body element of a FIELDSET) */
1490
-body.chat .message-content {
1490
+body.chat .message-widget-content {
14911491
display: inline-block;
14921492
border-radius: 0.25em;
14931493
border: 1px solid rgba(0,0,0,0.2);
14941494
box-shadow: 0.2em 0.2em 0.2em rgba(0, 0, 0, 0.29);
14951495
padding: 0.25em 0.5em;
1496
- margin-top: -0.75em/*slide it up to the base of the LEGEND*/;
1496
+ margin-top: 0;
14971497
min-width: 9em /*avoid unsightly "underlap" with the user name label*/;
14981498
white-space: pre-wrap/*needed for multi-line edits*/;
14991499
}
1500
-body.chat.monospace-messages .message-content,
1500
+body.chat.monospace-messages .message-widget-content,
15011501
body.chat.monospace-messages textarea,
15021502
body.chat.monospace-messages input[type=text]{
15031503
font-family: monospace;
15041504
}
15051505
15061506
/* User name for the post (a LEGEND element) */
1507
-body.chat .message-row .message-user {
1507
+body.chat .message-widget .message-widget-tab {
15081508
border-radius: 0.25em 0.25em 0 0;
15091509
padding: 0 0.5em;
15101510
/*text-align: left; Firefox requires the 'align' attribute */
1511
- margin: 0 0.25em 0.4em 0.15em;
1512
- padding: 0 0.5em 0em 0.5em;
1511
+ margin: 0 0.25em 0em 0.15em;
1512
+ padding: 0 0.5em 0.15em 0.5em;
15131513
cursor: pointer;
15141514
}
15151515
15161516
body.chat .fossil-tooltip.help-buttonlet-content {
15171517
font-size: 80%;
15181518
--- src/default.css
+++ src/default.css
@@ -1473,45 +1473,45 @@
1473 body.chat span.at-name { /* for @USERNAME references */
1474 text-decoration: underline;
1475 font-weight: bold;
1476 }
1477 /* A wrapper for a single single message (one row of the UI) */
1478 body.chat .message-row {
1479 margin-bottom: 0.5em;
1480 border: none;
1481 display: flex;
1482 flex-direction: row;
1483 justify-content: flex-start;
1484 /*border: 1px solid rgba(0,0,0,0.2);
1485 border-radius: 0.25em;
1486 box-shadow: 0.2em 0.2em 0.2em rgba(0, 0, 0, 0.29);*/
1487 border: none;
1488 }
1489 /* The content area of a message (the body element of a FIELDSET) */
1490 body.chat .message-content {
1491 display: inline-block;
1492 border-radius: 0.25em;
1493 border: 1px solid rgba(0,0,0,0.2);
1494 box-shadow: 0.2em 0.2em 0.2em rgba(0, 0, 0, 0.29);
1495 padding: 0.25em 0.5em;
1496 margin-top: -0.75em/*slide it up to the base of the LEGEND*/;
1497 min-width: 9em /*avoid unsightly "underlap" with the user name label*/;
1498 white-space: pre-wrap/*needed for multi-line edits*/;
1499 }
1500 body.chat.monospace-messages .message-content,
1501 body.chat.monospace-messages textarea,
1502 body.chat.monospace-messages input[type=text]{
1503 font-family: monospace;
1504 }
1505
1506 /* User name for the post (a LEGEND element) */
1507 body.chat .message-row .message-user {
1508 border-radius: 0.25em 0.25em 0 0;
1509 padding: 0 0.5em;
1510 /*text-align: left; Firefox requires the 'align' attribute */
1511 margin: 0 0.25em 0.4em 0.15em;
1512 padding: 0 0.5em 0em 0.5em;
1513 cursor: pointer;
1514 }
1515
1516 body.chat .fossil-tooltip.help-buttonlet-content {
1517 font-size: 80%;
1518
--- src/default.css
+++ src/default.css
@@ -1473,45 +1473,45 @@
1473 body.chat span.at-name { /* for @USERNAME references */
1474 text-decoration: underline;
1475 font-weight: bold;
1476 }
1477 /* A wrapper for a single single message (one row of the UI) */
1478 body.chat .message-widget {
1479 margin-bottom: 0.75em;
1480 border: none;
1481 display: flex;
1482 flex-direction: column;
1483 border: none;
1484 align-items: flex-start;
1485 }
1486 body.chat.my-messages-right .message-widget.mine {
1487 align-items: flex-end;
1488 }
1489 /* The content area of a message (the body element of a FIELDSET) */
1490 body.chat .message-widget-content {
1491 display: inline-block;
1492 border-radius: 0.25em;
1493 border: 1px solid rgba(0,0,0,0.2);
1494 box-shadow: 0.2em 0.2em 0.2em rgba(0, 0, 0, 0.29);
1495 padding: 0.25em 0.5em;
1496 margin-top: 0;
1497 min-width: 9em /*avoid unsightly "underlap" with the user name label*/;
1498 white-space: pre-wrap/*needed for multi-line edits*/;
1499 }
1500 body.chat.monospace-messages .message-widget-content,
1501 body.chat.monospace-messages textarea,
1502 body.chat.monospace-messages input[type=text]{
1503 font-family: monospace;
1504 }
1505
1506 /* User name for the post (a LEGEND element) */
1507 body.chat .message-widget .message-widget-tab {
1508 border-radius: 0.25em 0.25em 0 0;
1509 padding: 0 0.5em;
1510 /*text-align: left; Firefox requires the 'align' attribute */
1511 margin: 0 0.25em 0em 0.15em;
1512 padding: 0 0.5em 0.15em 0.5em;
1513 cursor: pointer;
1514 }
1515
1516 body.chat .fossil-tooltip.help-buttonlet-content {
1517 font-size: 80%;
1518

Keyboard Shortcuts

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