Fossil SCM

Several minor cleanups, fixes, and presentation tweaks.

stephan 2021-09-24 17:01 chat-user-filter
Commit 10107e4fbc80d0b87dc0fae3a669d7d6d1b5eb5990f20553ed769129f69f9dac
+10 -7
--- src/chat.c
+++ src/chat.c
@@ -182,18 +182,21 @@
182182
@ </div>
183183
@ <div id="chat-drop-details"></div>
184184
@ </div>
185185
@ </div>
186186
@ <div id='chat-user-list-wrapper' class='hidden'>
187
- @ <legend>Active users (sorted by last message time)</legend>
188
- @ <div id='chat-user-list'>
189
- @ <div class='help-buttonlet'>
190
- @ Users who have messages in the currently-loaded list.<br>
191
- @ Tap a user name to filter messages on that user and
192
- @ tap again to clear the filter.
193
- @ </div>
187
+ @ <div class='legend'>
188
+ @ <span class='help-buttonlet'>
189
+ @ Users who have messages in the currently-loaded list.<br><br>
190
+ @ <strong>Tap a user name</strong> to filter messages
191
+ @ on that user and tap again to clear the filter.<br><br>
192
+ @ <strong>Tap the title</strong> of this widget to toggle
193
+ @ the list on and off.
194
+ @ </span>
195
+ @ <span>Active users (sorted by last message time)</span>
194196
@ </div>
197
+ @ <div id='chat-user-list'></div>
195198
@ </div>
196199
@ <div id='chat-preview' class='hidden chat-view'>
197200
@ <header>Preview: (<a href='%R/md_rules' target='_blank'>markdown reference</a>)</header>
198201
@ <div id='chat-preview-content' class='message-widget-content'></div>
199202
@ <div id='chat-preview-buttons'><button id='chat-preview-close'>Close Preview</button></div>
200203
--- src/chat.c
+++ src/chat.c
@@ -182,18 +182,21 @@
182 @ </div>
183 @ <div id="chat-drop-details"></div>
184 @ </div>
185 @ </div>
186 @ <div id='chat-user-list-wrapper' class='hidden'>
187 @ <legend>Active users (sorted by last message time)</legend>
188 @ <div id='chat-user-list'>
189 @ <div class='help-buttonlet'>
190 @ Users who have messages in the currently-loaded list.<br>
191 @ Tap a user name to filter messages on that user and
192 @ tap again to clear the filter.
193 @ </div>
 
 
194 @ </div>
 
195 @ </div>
196 @ <div id='chat-preview' class='hidden chat-view'>
197 @ <header>Preview: (<a href='%R/md_rules' target='_blank'>markdown reference</a>)</header>
198 @ <div id='chat-preview-content' class='message-widget-content'></div>
199 @ <div id='chat-preview-buttons'><button id='chat-preview-close'>Close Preview</button></div>
200
--- src/chat.c
+++ src/chat.c
@@ -182,18 +182,21 @@
182 @ </div>
183 @ <div id="chat-drop-details"></div>
184 @ </div>
185 @ </div>
186 @ <div id='chat-user-list-wrapper' class='hidden'>
187 @ <div class='legend'>
188 @ <span class='help-buttonlet'>
189 @ Users who have messages in the currently-loaded list.<br><br>
190 @ <strong>Tap a user name</strong> to filter messages
191 @ on that user and tap again to clear the filter.<br><br>
192 @ <strong>Tap the title</strong> of this widget to toggle
193 @ the list on and off.
194 @ </span>
195 @ <span>Active users (sorted by last message time)</span>
196 @ </div>
197 @ <div id='chat-user-list'></div>
198 @ </div>
199 @ <div id='chat-preview' class='hidden chat-view'>
200 @ <header>Preview: (<a href='%R/md_rules' target='_blank'>markdown reference</a>)</header>
201 @ <div id='chat-preview-content' class='message-widget-content'></div>
202 @ <div id='chat-preview-buttons'><button id='chat-preview-close'>Close Preview</button></div>
203
+27 -8
--- src/chat.js
+++ src/chat.js
@@ -447,11 +447,13 @@
447447
*/
448448
setCurrentView: function(e){
449449
this.e.views.forEach(function(E){
450450
if(e!==E) D.addClass(E,'hidden');
451451
});
452
- return this.e.currentView = D.removeClass(e,'hidden');
452
+ this.e.currentView = D.removeClass(e,'hidden');
453
+ this.animate(this.e.currentView, 'anim-fade-in-fast');
454
+ return this.e.currentView;
453455
},
454456
/**
455457
Updates the "active user list" view if we are not currently
456458
batch-loading messages and if the active user list UI element
457459
is active.
@@ -528,15 +530,16 @@
528530
},
529531
530532
/**
531533
If animations are enabled, passes its arguments
532534
to D.addClassBriefly(), else this is a no-op.
533
- Returns this object;
535
+ If cb is a function, it is called after the
536
+ CSS class is removed. Returns this object;
534537
*/
535
- animate: function f(e,a){
538
+ animate: function f(e,a,cb){
536539
if(!f.$disabled){
537
- D.addClassBriefly(e, a);
540
+ D.addClassBriefly(e, a, 0, cb);
538541
}
539542
return this;
540543
}
541544
};
542545
cs.animate.$disabled = true;
@@ -1008,11 +1011,11 @@
10081011
function(){
10091012
self.hide();
10101013
Chat.setUserFilter(false);
10111014
eMsg.scrollIntoView(false);
10121015
Chat.animate(
1013
- eMsg.firstElementChild, 'anim-rotate-360'
1016
+ eMsg.firstElementChild, 'anim-flip-h'
10141017
//eMsg.firstElementChild, 'anim-flip-v'
10151018
//eMsg.childNodes, 'anim-rotate-360'
10161019
//eMsg.childNodes, 'anim-flip-v'
10171020
//eMsg, 'anim-flip-v'
10181021
);
@@ -1019,18 +1022,19 @@
10191022
})
10201023
)
10211024
);
10221025
}/*jump-to button*/
10231026
}
1024
-
10251027
const tab = eMsg.querySelector('.message-widget-tab');
10261028
D.append(tab, this.e);
10271029
D.removeClass(this.e, 'hidden');
1030
+ Chat.animate(this.e, 'anim-fade-in-fast');
10281031
}/*refresh()*/,
10291032
hide: function(){
1030
- D.addClass(D.clearElement(this.e), 'hidden');
10311033
delete this.$eMsg;
1034
+ D.addClass(this.e, 'hidden');
1035
+ D.clearElement(this.e);
10321036
},
10331037
show: function(tgtMsg){
10341038
if(tgtMsg === this.$eMsg){
10351039
this.hide();
10361040
return;
@@ -1232,24 +1236,38 @@
12321236
label: "Show active users list",
12331237
boolValue: ()=>!Chat.e.activeUserListWrapper.classList.contains('hidden'),
12341238
persistentSetting: 'active-user-list',
12351239
callback: function(){
12361240
D.toggleClass(Chat.e.activeUserListWrapper,'hidden');
1241
+ D.removeClass(Chat.e.activeUserListWrapper, 'collapsed');
12371242
if(Chat.e.activeUserListWrapper.classList.contains('hidden')){
12381243
/* When hiding this element, undo all filtering */
12391244
Chat.setUserFilter(false);
12401245
/*Ideally we'd scroll the final message into view
12411246
now, but because viewMessages is currently hidden behind
12421247
viewConfig, scrolling is a no-op. */
12431248
Chat.scrollMessagesTo(1);
12441249
}else{
12451250
Chat.updateActiveUserList();
1246
- Chat.animate(Chat.e.activeUserListWrapper, "anim-flip-v");
1251
+ Chat.animate(Chat.e.activeUserListWrapper, 'anim-flip-v');
12471252
}
12481253
}
12491254
}
12501255
};
1256
+ if(1){
1257
+ /* Per user request, toggle the list of users on and off if the
1258
+ legend element is tapped. */
1259
+ const optAu = namedOptions.activeUsers;
1260
+ optAu.theLegend = Chat.e.activeUserListWrapper.firstElementChild/*LEGEND*/;
1261
+ optAu.theList = optAu.theLegend.nextElementSibling/*user list container*/;
1262
+ optAu.theLegend.addEventListener('click',function(){
1263
+ D.toggleClass(Chat.e.activeUserListWrapper, 'collapsed');
1264
+ if(!Chat.e.activeUserListWrapper.classList.contains('collapsed')){
1265
+ Chat.animate(optAu.theList,'anim-flip-v');
1266
+ }
1267
+ }, false);
1268
+ }/*namedOptions.activeUsers additional setup*/
12511269
/* Settings menu entries... Remember that they will be rendered in
12521270
reverse order and the most frequently-needed ones "should"
12531271
(arguably) be closer to the start of this list so that they
12541272
will be rendered within easier reach of the settings button. */
12551273
const settingsOps = [{
@@ -1283,10 +1301,11 @@
12831301
toggle that option on as well. */
12841302
if(Chat.e.activeUserList.classList.contains('timestamps')
12851303
&& !namedOptions.activeUsers.boolValue()){
12861304
namedOptions.activeUsers.checkbox.checked = true;
12871305
namedOptions.activeUsers.callback();
1306
+ Chat.settings.set(namedOptions.activeUsers.persistentSetting, true);
12881307
}
12891308
}
12901309
},
12911310
namedOptions.activeUsers,{
12921311
label: "Monospace message font",
12931312
--- src/chat.js
+++ src/chat.js
@@ -447,11 +447,13 @@
447 */
448 setCurrentView: function(e){
449 this.e.views.forEach(function(E){
450 if(e!==E) D.addClass(E,'hidden');
451 });
452 return this.e.currentView = D.removeClass(e,'hidden');
 
 
453 },
454 /**
455 Updates the "active user list" view if we are not currently
456 batch-loading messages and if the active user list UI element
457 is active.
@@ -528,15 +530,16 @@
528 },
529
530 /**
531 If animations are enabled, passes its arguments
532 to D.addClassBriefly(), else this is a no-op.
533 Returns this object;
 
534 */
535 animate: function f(e,a){
536 if(!f.$disabled){
537 D.addClassBriefly(e, a);
538 }
539 return this;
540 }
541 };
542 cs.animate.$disabled = true;
@@ -1008,11 +1011,11 @@
1008 function(){
1009 self.hide();
1010 Chat.setUserFilter(false);
1011 eMsg.scrollIntoView(false);
1012 Chat.animate(
1013 eMsg.firstElementChild, 'anim-rotate-360'
1014 //eMsg.firstElementChild, 'anim-flip-v'
1015 //eMsg.childNodes, 'anim-rotate-360'
1016 //eMsg.childNodes, 'anim-flip-v'
1017 //eMsg, 'anim-flip-v'
1018 );
@@ -1019,18 +1022,19 @@
1019 })
1020 )
1021 );
1022 }/*jump-to button*/
1023 }
1024
1025 const tab = eMsg.querySelector('.message-widget-tab');
1026 D.append(tab, this.e);
1027 D.removeClass(this.e, 'hidden');
 
1028 }/*refresh()*/,
1029 hide: function(){
1030 D.addClass(D.clearElement(this.e), 'hidden');
1031 delete this.$eMsg;
 
 
1032 },
1033 show: function(tgtMsg){
1034 if(tgtMsg === this.$eMsg){
1035 this.hide();
1036 return;
@@ -1232,24 +1236,38 @@
1232 label: "Show active users list",
1233 boolValue: ()=>!Chat.e.activeUserListWrapper.classList.contains('hidden'),
1234 persistentSetting: 'active-user-list',
1235 callback: function(){
1236 D.toggleClass(Chat.e.activeUserListWrapper,'hidden');
 
1237 if(Chat.e.activeUserListWrapper.classList.contains('hidden')){
1238 /* When hiding this element, undo all filtering */
1239 Chat.setUserFilter(false);
1240 /*Ideally we'd scroll the final message into view
1241 now, but because viewMessages is currently hidden behind
1242 viewConfig, scrolling is a no-op. */
1243 Chat.scrollMessagesTo(1);
1244 }else{
1245 Chat.updateActiveUserList();
1246 Chat.animate(Chat.e.activeUserListWrapper, "anim-flip-v");
1247 }
1248 }
1249 }
1250 };
 
 
 
 
 
 
 
 
 
 
 
 
 
1251 /* Settings menu entries... Remember that they will be rendered in
1252 reverse order and the most frequently-needed ones "should"
1253 (arguably) be closer to the start of this list so that they
1254 will be rendered within easier reach of the settings button. */
1255 const settingsOps = [{
@@ -1283,10 +1301,11 @@
1283 toggle that option on as well. */
1284 if(Chat.e.activeUserList.classList.contains('timestamps')
1285 && !namedOptions.activeUsers.boolValue()){
1286 namedOptions.activeUsers.checkbox.checked = true;
1287 namedOptions.activeUsers.callback();
 
1288 }
1289 }
1290 },
1291 namedOptions.activeUsers,{
1292 label: "Monospace message font",
1293
--- src/chat.js
+++ src/chat.js
@@ -447,11 +447,13 @@
447 */
448 setCurrentView: function(e){
449 this.e.views.forEach(function(E){
450 if(e!==E) D.addClass(E,'hidden');
451 });
452 this.e.currentView = D.removeClass(e,'hidden');
453 this.animate(this.e.currentView, 'anim-fade-in-fast');
454 return this.e.currentView;
455 },
456 /**
457 Updates the "active user list" view if we are not currently
458 batch-loading messages and if the active user list UI element
459 is active.
@@ -528,15 +530,16 @@
530 },
531
532 /**
533 If animations are enabled, passes its arguments
534 to D.addClassBriefly(), else this is a no-op.
535 If cb is a function, it is called after the
536 CSS class is removed. Returns this object;
537 */
538 animate: function f(e,a,cb){
539 if(!f.$disabled){
540 D.addClassBriefly(e, a, 0, cb);
541 }
542 return this;
543 }
544 };
545 cs.animate.$disabled = true;
@@ -1008,11 +1011,11 @@
1011 function(){
1012 self.hide();
1013 Chat.setUserFilter(false);
1014 eMsg.scrollIntoView(false);
1015 Chat.animate(
1016 eMsg.firstElementChild, 'anim-flip-h'
1017 //eMsg.firstElementChild, 'anim-flip-v'
1018 //eMsg.childNodes, 'anim-rotate-360'
1019 //eMsg.childNodes, 'anim-flip-v'
1020 //eMsg, 'anim-flip-v'
1021 );
@@ -1019,18 +1022,19 @@
1022 })
1023 )
1024 );
1025 }/*jump-to button*/
1026 }
 
1027 const tab = eMsg.querySelector('.message-widget-tab');
1028 D.append(tab, this.e);
1029 D.removeClass(this.e, 'hidden');
1030 Chat.animate(this.e, 'anim-fade-in-fast');
1031 }/*refresh()*/,
1032 hide: function(){
 
1033 delete this.$eMsg;
1034 D.addClass(this.e, 'hidden');
1035 D.clearElement(this.e);
1036 },
1037 show: function(tgtMsg){
1038 if(tgtMsg === this.$eMsg){
1039 this.hide();
1040 return;
@@ -1232,24 +1236,38 @@
1236 label: "Show active users list",
1237 boolValue: ()=>!Chat.e.activeUserListWrapper.classList.contains('hidden'),
1238 persistentSetting: 'active-user-list',
1239 callback: function(){
1240 D.toggleClass(Chat.e.activeUserListWrapper,'hidden');
1241 D.removeClass(Chat.e.activeUserListWrapper, 'collapsed');
1242 if(Chat.e.activeUserListWrapper.classList.contains('hidden')){
1243 /* When hiding this element, undo all filtering */
1244 Chat.setUserFilter(false);
1245 /*Ideally we'd scroll the final message into view
1246 now, but because viewMessages is currently hidden behind
1247 viewConfig, scrolling is a no-op. */
1248 Chat.scrollMessagesTo(1);
1249 }else{
1250 Chat.updateActiveUserList();
1251 Chat.animate(Chat.e.activeUserListWrapper, 'anim-flip-v');
1252 }
1253 }
1254 }
1255 };
1256 if(1){
1257 /* Per user request, toggle the list of users on and off if the
1258 legend element is tapped. */
1259 const optAu = namedOptions.activeUsers;
1260 optAu.theLegend = Chat.e.activeUserListWrapper.firstElementChild/*LEGEND*/;
1261 optAu.theList = optAu.theLegend.nextElementSibling/*user list container*/;
1262 optAu.theLegend.addEventListener('click',function(){
1263 D.toggleClass(Chat.e.activeUserListWrapper, 'collapsed');
1264 if(!Chat.e.activeUserListWrapper.classList.contains('collapsed')){
1265 Chat.animate(optAu.theList,'anim-flip-v');
1266 }
1267 }, false);
1268 }/*namedOptions.activeUsers additional setup*/
1269 /* Settings menu entries... Remember that they will be rendered in
1270 reverse order and the most frequently-needed ones "should"
1271 (arguably) be closer to the start of this list so that they
1272 will be rendered within easier reach of the settings button. */
1273 const settingsOps = [{
@@ -1283,10 +1301,11 @@
1301 toggle that option on as well. */
1302 if(Chat.e.activeUserList.classList.contains('timestamps')
1303 && !namedOptions.activeUsers.boolValue()){
1304 namedOptions.activeUsers.checkbox.checked = true;
1305 namedOptions.activeUsers.callback();
1306 Chat.settings.set(namedOptions.activeUsers.persistentSetting, true);
1307 }
1308 }
1309 },
1310 namedOptions.activeUsers,{
1311 label: "Monospace message font",
1312
+1 -1
--- src/default.css
+++ src/default.css
@@ -1387,11 +1387,11 @@
13871387
13881388
.fossil-tooltip {
13891389
text-align: center;
13901390
padding: 0.2em 1em;
13911391
border: 1px solid black;
1392
- border-radius: 0.25em;
1392
+ border-radius: 0.5em;
13931393
position: absolute;
13941394
display: inline-block;
13951395
z-index: 19/*below default skin's hamburger popup*/;
13961396
box-shadow: -0.15em 0.15em 0.2em rgba(0, 0, 0, 0.75);
13971397
background-color: inherit;
13981398
--- src/default.css
+++ src/default.css
@@ -1387,11 +1387,11 @@
1387
1388 .fossil-tooltip {
1389 text-align: center;
1390 padding: 0.2em 1em;
1391 border: 1px solid black;
1392 border-radius: 0.25em;
1393 position: absolute;
1394 display: inline-block;
1395 z-index: 19/*below default skin's hamburger popup*/;
1396 box-shadow: -0.15em 0.15em 0.2em rgba(0, 0, 0, 0.75);
1397 background-color: inherit;
1398
--- src/default.css
+++ src/default.css
@@ -1387,11 +1387,11 @@
1387
1388 .fossil-tooltip {
1389 text-align: center;
1390 padding: 0.2em 1em;
1391 border: 1px solid black;
1392 border-radius: 0.5em;
1393 position: absolute;
1394 display: inline-block;
1395 z-index: 19/*below default skin's hamburger popup*/;
1396 box-shadow: -0.15em 0.15em 0.2em rgba(0, 0, 0, 0.75);
1397 background-color: inherit;
1398
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -730,10 +730,13 @@
730730
CSS class is removed from all elements. If called with 3
731731
arguments and the 3rd is a function, the 3rd is treated as a
732732
callback and the default time (addClassBriefly.defaultTimeMs) is
733733
used. If called with only 2 arguments, a time of
734734
addClassBriefly.defaultTimeMs is used.
735
+
736
+ Passing a value of 0 for howLongMs causes the default value
737
+ to be applied.
735738
736739
Returns this object but the CSS removal is asynchronous.
737740
*/
738741
dom.addClassBriefly = function f(e, className, howLongMs, afterCallback){
739742
if(arguments.length<4 && 'function'===typeof howLongMs){
@@ -740,11 +743,10 @@
740743
afterCallback = howLongMs;
741744
howLongMs = f.defaultTimeMs;
742745
}else if(arguments.length<3 || !+howLongMs){
743746
howLongMs = f.defaultTimeMs;
744747
}
745
- if(!e.forEach) e = [e];
746748
this.addClass(e, className);
747749
setTimeout(function(){
748750
dom.removeClass(e, className);
749751
if(afterCallback) afterCallback();
750752
}, howLongMs);
751753
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -730,10 +730,13 @@
730 CSS class is removed from all elements. If called with 3
731 arguments and the 3rd is a function, the 3rd is treated as a
732 callback and the default time (addClassBriefly.defaultTimeMs) is
733 used. If called with only 2 arguments, a time of
734 addClassBriefly.defaultTimeMs is used.
 
 
 
735
736 Returns this object but the CSS removal is asynchronous.
737 */
738 dom.addClassBriefly = function f(e, className, howLongMs, afterCallback){
739 if(arguments.length<4 && 'function'===typeof howLongMs){
@@ -740,11 +743,10 @@
740 afterCallback = howLongMs;
741 howLongMs = f.defaultTimeMs;
742 }else if(arguments.length<3 || !+howLongMs){
743 howLongMs = f.defaultTimeMs;
744 }
745 if(!e.forEach) e = [e];
746 this.addClass(e, className);
747 setTimeout(function(){
748 dom.removeClass(e, className);
749 if(afterCallback) afterCallback();
750 }, howLongMs);
751
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -730,10 +730,13 @@
730 CSS class is removed from all elements. If called with 3
731 arguments and the 3rd is a function, the 3rd is treated as a
732 callback and the default time (addClassBriefly.defaultTimeMs) is
733 used. If called with only 2 arguments, a time of
734 addClassBriefly.defaultTimeMs is used.
735
736 Passing a value of 0 for howLongMs causes the default value
737 to be applied.
738
739 Returns this object but the CSS removal is asynchronous.
740 */
741 dom.addClassBriefly = function f(e, className, howLongMs, afterCallback){
742 if(arguments.length<4 && 'function'===typeof howLongMs){
@@ -740,11 +743,10 @@
743 afterCallback = howLongMs;
744 howLongMs = f.defaultTimeMs;
745 }else if(arguments.length<3 || !+howLongMs){
746 howLongMs = f.defaultTimeMs;
747 }
 
748 this.addClass(e, className);
749 setTimeout(function(){
750 dom.removeClass(e, className);
751 if(afterCallback) afterCallback();
752 }, howLongMs);
753
--- src/fossil.popupwidget.js
+++ src/fossil.popupwidget.js
@@ -355,10 +355,11 @@
355355
*/
356356
setup: function f(){
357357
if(!f.hasOwnProperty('clickHandler')){
358358
f.clickHandler = function fch(ev){
359359
ev.preventDefault();
360
+ ev.stopPropagation();
360361
if(!fch.popup){
361362
fch.popup = new F.PopupWidget({
362363
cssClass: ['fossil-tooltip', 'help-buttonlet-content'],
363364
refresh: function(){
364365
}
@@ -411,10 +412,11 @@
411412
x -= popupRect.width/2;
412413
}
413414
if(x<0) x = 0;
414415
//console.debug("dimensions",x,y, popupRect, rectBody);
415416
fch.popup.show(x, y);
417
+ return false;
416418
};
417419
f.foreachElement = function(e){
418420
if(e.classList.contains('processed')) return;
419421
e.classList.add('processed');
420422
e.$helpContent = [];
421423
--- src/fossil.popupwidget.js
+++ src/fossil.popupwidget.js
@@ -355,10 +355,11 @@
355 */
356 setup: function f(){
357 if(!f.hasOwnProperty('clickHandler')){
358 f.clickHandler = function fch(ev){
359 ev.preventDefault();
 
360 if(!fch.popup){
361 fch.popup = new F.PopupWidget({
362 cssClass: ['fossil-tooltip', 'help-buttonlet-content'],
363 refresh: function(){
364 }
@@ -411,10 +412,11 @@
411 x -= popupRect.width/2;
412 }
413 if(x<0) x = 0;
414 //console.debug("dimensions",x,y, popupRect, rectBody);
415 fch.popup.show(x, y);
 
416 };
417 f.foreachElement = function(e){
418 if(e.classList.contains('processed')) return;
419 e.classList.add('processed');
420 e.$helpContent = [];
421
--- src/fossil.popupwidget.js
+++ src/fossil.popupwidget.js
@@ -355,10 +355,11 @@
355 */
356 setup: function f(){
357 if(!f.hasOwnProperty('clickHandler')){
358 f.clickHandler = function fch(ev){
359 ev.preventDefault();
360 ev.stopPropagation();
361 if(!fch.popup){
362 fch.popup = new F.PopupWidget({
363 cssClass: ['fossil-tooltip', 'help-buttonlet-content'],
364 refresh: function(){
365 }
@@ -411,10 +412,11 @@
412 x -= popupRect.width/2;
413 }
414 if(x<0) x = 0;
415 //console.debug("dimensions",x,y, popupRect, rectBody);
416 fch.popup.show(x, y);
417 return false;
418 };
419 f.foreachElement = function(e){
420 if(e.classList.contains('processed')) return;
421 e.classList.add('processed');
422 e.$helpContent = [];
423
+60 -29
--- src/style.chat.css
+++ src/style.chat.css
@@ -9,14 +9,18 @@
99
border: none;
1010
display: flex;
1111
flex-direction: column;
1212
border: none;
1313
align-items: flex-start;
14
+}
15
+body.chat .message-widget:last-of-type {
16
+ /* Latest message: reduce bottom gap */
17
+ margin-bottom: 0.1em;
1418
}
1519
body.chat.my-messages-right .message-widget.mine {
1620
/* Right-aligns a user's own chat messages, similar to how
17
- most mobile messaging apps do it. */
21
+ most/some mobile messaging apps do it. */
1822
align-items: flex-end;
1923
}
2024
body.chat.my-messages-right .message-widget.notification {
2125
/* Center-aligns a system-level notification message. */
2226
align-items: center;
@@ -77,10 +81,12 @@
7781
display: flex;
7882
flex-direction: column;
7983
align-items: stretch;
8084
padding: 0.25em;
8185
margin-top: 0.25em;
86
+ border: 1px outset;
87
+ border-radius: 0.5em;
8288
}
8389
/* Full message timestamps. */
8490
body.chat .chat-message-popup > span { white-space: nowrap; }
8591
/* Container for the message deletion buttons. */
8692
body.chat .chat-message-popup > .toolbar {
@@ -137,11 +143,10 @@
137143
138144
/** Container for the list of /chat messages. */
139145
body.chat #chat-messages-wrapper {
140146
overflow: auto;
141147
padding: 0 0.25em;
142
- margin-bottom: 0.5em;
143148
}
144149
body.chat #chat-messages-wrapper.loading > * {
145150
/* An attempt at reducing flicker when loading lots of messages. */
146151
visibility: hidden;
147152
}
@@ -267,10 +272,11 @@
267272
max-height: 45%;
268273
}
269274
body.chat .chat-view {
270275
flex: 20 1 auto
271276
/*ensure that these grow more than the non-.chat-view elements*/;
277
+ margin-bottom: 0.2em;
272278
}
273279
body.chat #chat-config,
274280
body.chat #chat-preview {
275281
/* /chat configuration widget */
276282
display: flex;
@@ -327,33 +333,53 @@
327333
}
328334
329335
body.chat #chat-user-list-wrapper {
330336
/* Safari can't do fieldsets right, so we emulate one. */
331337
border-radius: 0.5em;
332
- margin: 0.55em 0 0.2em 0;
333
- padding: 0.25em 0.5em;
334
- font-size: 85%;
338
+ margin: 1em 0 0.2em 0;
339
+ padding: 0 0.5em;
335340
border-style: inset;
336341
border-width: 0 1px 1px 1px/*else collides with the LEGEND*/;
337342
}
338
-body.chat #chat-user-list-wrapper > legend {
343
+body.chat #chat-user-list-wrapper.collapsed {
344
+ padding: 0;
345
+ margin-bottom: 0;
346
+}
347
+body.chat #chat-user-list-wrapper > .legend {
339348
font-weight: initial;
340
- padding: 0 0.5em;
349
+ padding: 0 0.5em 0 0.5em;
341350
position: relative;
342
- top: -1.75ex;
351
+ top: -1.75ex/* place it like a fieldset legend */;
352
+ cursor: pointer;
353
+}
354
+body.chat #chat-user-list-wrapper > .legend > * {
355
+ vertical-align: middle;
356
+}
357
+body.chat #chat-user-list-wrapper > .legend > *:nth-child(2){
358
+ /* Title label */
343359
opacity: 0.6;
360
+ font-size: 0.8em;
361
+}
362
+body.chat #chat-user-list-wrapper.collapsed > .legend > *:nth-child(2)::after {
363
+ content: " (tap to toggle)";
364
+}
365
+body.chat #chat-user-list-wrapper .help-buttonlet {
366
+ margin: 0;
367
+}
368
+body.chat #chat-user-list-wrapper.collapsed #chat-user-list {
369
+ position: absolute !important;
370
+ opacity: 0 !important;
371
+ pointer-events: none !important;
372
+ display: none !important;
344373
}
345374
body.chat #chat-user-list {
346375
margin-top: -1.25ex;
347376
display: flex;
348377
flex-direction: row;
349378
flex-wrap: wrap;
350379
align-items: center;
351380
}
352
-body.chat #chat-user-list .help-buttonlet {
353
- margin: 0.2em 0.5em 0.2em 0;
354
-}
355381
body.chat #chat-user-list .chat-user {
356382
margin: 0.2em;
357383
padding: 0.1em 0.5em 0.2em 0.5em;
358384
border-radius: 0.5em;
359385
cursor: pointer;
@@ -374,34 +400,39 @@
374400
375401
body.chat .anim-rotate-360 {
376402
animation: rotate-360 750ms linear;
377403
}
378404
@keyframes rotate-360 {
379
- from {
380
- transform: rotate(0deg);
381
- }
382
- to {
383
- transform: rotate(360deg);
384
- }
405
+ from { transform: rotate(0deg); }
406
+ to { transform: rotate(360deg); }
385407
}
386408
body.chat .anim-flip-h {
387409
animation: flip-h 750ms linear;
388410
}
389411
@keyframes flip-h{
390
- from{
391
- transform: rotateY(0deg);
392
- }
393
- to{
394
- transform: rotateY(360deg);
395
- }
412
+ from { transform: rotateY(0deg); }
413
+ to { transform: rotateY(360deg); }
396414
}
397415
body.chat .anim-flip-v {
398416
animation: flip-v 750ms linear;
399417
}
400418
@keyframes flip-v{
401
- from{
402
- transform: rotateX(0deg);
403
- }
404
- to{
405
- transform: rotateX(360deg);
406
- }
419
+ from { transform: rotateX(0deg); }
420
+ to { transform: rotateX(360deg); }
421
+}
422
+body.chat .anim-fade-in {
423
+ animation: fade-in 750ms linear;
424
+}
425
+body.chat .anim-fade-in-fast {
426
+ animation: fade-in 350ms linear;
427
+}
428
+@keyframes fade-in {
429
+ from { opacity: 0; }
430
+ to { opacity: 1; }
431
+}
432
+body.chat .anim-fade-out-fast {
433
+ animation: fade-out 250ms linear;
434
+}
435
+@keyframes fade-out {
436
+ from { opacity: 1; }
437
+ to { opacity: 0; }
407438
}
408439
--- src/style.chat.css
+++ src/style.chat.css
@@ -9,14 +9,18 @@
9 border: none;
10 display: flex;
11 flex-direction: column;
12 border: none;
13 align-items: flex-start;
 
 
 
 
14 }
15 body.chat.my-messages-right .message-widget.mine {
16 /* Right-aligns a user's own chat messages, similar to how
17 most mobile messaging apps do it. */
18 align-items: flex-end;
19 }
20 body.chat.my-messages-right .message-widget.notification {
21 /* Center-aligns a system-level notification message. */
22 align-items: center;
@@ -77,10 +81,12 @@
77 display: flex;
78 flex-direction: column;
79 align-items: stretch;
80 padding: 0.25em;
81 margin-top: 0.25em;
 
 
82 }
83 /* Full message timestamps. */
84 body.chat .chat-message-popup > span { white-space: nowrap; }
85 /* Container for the message deletion buttons. */
86 body.chat .chat-message-popup > .toolbar {
@@ -137,11 +143,10 @@
137
138 /** Container for the list of /chat messages. */
139 body.chat #chat-messages-wrapper {
140 overflow: auto;
141 padding: 0 0.25em;
142 margin-bottom: 0.5em;
143 }
144 body.chat #chat-messages-wrapper.loading > * {
145 /* An attempt at reducing flicker when loading lots of messages. */
146 visibility: hidden;
147 }
@@ -267,10 +272,11 @@
267 max-height: 45%;
268 }
269 body.chat .chat-view {
270 flex: 20 1 auto
271 /*ensure that these grow more than the non-.chat-view elements*/;
 
272 }
273 body.chat #chat-config,
274 body.chat #chat-preview {
275 /* /chat configuration widget */
276 display: flex;
@@ -327,33 +333,53 @@
327 }
328
329 body.chat #chat-user-list-wrapper {
330 /* Safari can't do fieldsets right, so we emulate one. */
331 border-radius: 0.5em;
332 margin: 0.55em 0 0.2em 0;
333 padding: 0.25em 0.5em;
334 font-size: 85%;
335 border-style: inset;
336 border-width: 0 1px 1px 1px/*else collides with the LEGEND*/;
337 }
338 body.chat #chat-user-list-wrapper > legend {
 
 
 
 
339 font-weight: initial;
340 padding: 0 0.5em;
341 position: relative;
342 top: -1.75ex;
 
 
 
 
 
 
 
343 opacity: 0.6;
 
 
 
 
 
 
 
 
 
 
 
 
 
344 }
345 body.chat #chat-user-list {
346 margin-top: -1.25ex;
347 display: flex;
348 flex-direction: row;
349 flex-wrap: wrap;
350 align-items: center;
351 }
352 body.chat #chat-user-list .help-buttonlet {
353 margin: 0.2em 0.5em 0.2em 0;
354 }
355 body.chat #chat-user-list .chat-user {
356 margin: 0.2em;
357 padding: 0.1em 0.5em 0.2em 0.5em;
358 border-radius: 0.5em;
359 cursor: pointer;
@@ -374,34 +400,39 @@
374
375 body.chat .anim-rotate-360 {
376 animation: rotate-360 750ms linear;
377 }
378 @keyframes rotate-360 {
379 from {
380 transform: rotate(0deg);
381 }
382 to {
383 transform: rotate(360deg);
384 }
385 }
386 body.chat .anim-flip-h {
387 animation: flip-h 750ms linear;
388 }
389 @keyframes flip-h{
390 from{
391 transform: rotateY(0deg);
392 }
393 to{
394 transform: rotateY(360deg);
395 }
396 }
397 body.chat .anim-flip-v {
398 animation: flip-v 750ms linear;
399 }
400 @keyframes flip-v{
401 from{
402 transform: rotateX(0deg);
403 }
404 to{
405 transform: rotateX(360deg);
406 }
 
 
 
 
 
 
 
 
 
 
 
 
 
407 }
408
--- src/style.chat.css
+++ src/style.chat.css
@@ -9,14 +9,18 @@
9 border: none;
10 display: flex;
11 flex-direction: column;
12 border: none;
13 align-items: flex-start;
14 }
15 body.chat .message-widget:last-of-type {
16 /* Latest message: reduce bottom gap */
17 margin-bottom: 0.1em;
18 }
19 body.chat.my-messages-right .message-widget.mine {
20 /* Right-aligns a user's own chat messages, similar to how
21 most/some mobile messaging apps do it. */
22 align-items: flex-end;
23 }
24 body.chat.my-messages-right .message-widget.notification {
25 /* Center-aligns a system-level notification message. */
26 align-items: center;
@@ -77,10 +81,12 @@
81 display: flex;
82 flex-direction: column;
83 align-items: stretch;
84 padding: 0.25em;
85 margin-top: 0.25em;
86 border: 1px outset;
87 border-radius: 0.5em;
88 }
89 /* Full message timestamps. */
90 body.chat .chat-message-popup > span { white-space: nowrap; }
91 /* Container for the message deletion buttons. */
92 body.chat .chat-message-popup > .toolbar {
@@ -137,11 +143,10 @@
143
144 /** Container for the list of /chat messages. */
145 body.chat #chat-messages-wrapper {
146 overflow: auto;
147 padding: 0 0.25em;
 
148 }
149 body.chat #chat-messages-wrapper.loading > * {
150 /* An attempt at reducing flicker when loading lots of messages. */
151 visibility: hidden;
152 }
@@ -267,10 +272,11 @@
272 max-height: 45%;
273 }
274 body.chat .chat-view {
275 flex: 20 1 auto
276 /*ensure that these grow more than the non-.chat-view elements*/;
277 margin-bottom: 0.2em;
278 }
279 body.chat #chat-config,
280 body.chat #chat-preview {
281 /* /chat configuration widget */
282 display: flex;
@@ -327,33 +333,53 @@
333 }
334
335 body.chat #chat-user-list-wrapper {
336 /* Safari can't do fieldsets right, so we emulate one. */
337 border-radius: 0.5em;
338 margin: 1em 0 0.2em 0;
339 padding: 0 0.5em;
 
340 border-style: inset;
341 border-width: 0 1px 1px 1px/*else collides with the LEGEND*/;
342 }
343 body.chat #chat-user-list-wrapper.collapsed {
344 padding: 0;
345 margin-bottom: 0;
346 }
347 body.chat #chat-user-list-wrapper > .legend {
348 font-weight: initial;
349 padding: 0 0.5em 0 0.5em;
350 position: relative;
351 top: -1.75ex/* place it like a fieldset legend */;
352 cursor: pointer;
353 }
354 body.chat #chat-user-list-wrapper > .legend > * {
355 vertical-align: middle;
356 }
357 body.chat #chat-user-list-wrapper > .legend > *:nth-child(2){
358 /* Title label */
359 opacity: 0.6;
360 font-size: 0.8em;
361 }
362 body.chat #chat-user-list-wrapper.collapsed > .legend > *:nth-child(2)::after {
363 content: " (tap to toggle)";
364 }
365 body.chat #chat-user-list-wrapper .help-buttonlet {
366 margin: 0;
367 }
368 body.chat #chat-user-list-wrapper.collapsed #chat-user-list {
369 position: absolute !important;
370 opacity: 0 !important;
371 pointer-events: none !important;
372 display: none !important;
373 }
374 body.chat #chat-user-list {
375 margin-top: -1.25ex;
376 display: flex;
377 flex-direction: row;
378 flex-wrap: wrap;
379 align-items: center;
380 }
 
 
 
381 body.chat #chat-user-list .chat-user {
382 margin: 0.2em;
383 padding: 0.1em 0.5em 0.2em 0.5em;
384 border-radius: 0.5em;
385 cursor: pointer;
@@ -374,34 +400,39 @@
400
401 body.chat .anim-rotate-360 {
402 animation: rotate-360 750ms linear;
403 }
404 @keyframes rotate-360 {
405 from { transform: rotate(0deg); }
406 to { transform: rotate(360deg); }
 
 
 
 
407 }
408 body.chat .anim-flip-h {
409 animation: flip-h 750ms linear;
410 }
411 @keyframes flip-h{
412 from { transform: rotateY(0deg); }
413 to { transform: rotateY(360deg); }
 
 
 
 
414 }
415 body.chat .anim-flip-v {
416 animation: flip-v 750ms linear;
417 }
418 @keyframes flip-v{
419 from { transform: rotateX(0deg); }
420 to { transform: rotateX(360deg); }
421 }
422 body.chat .anim-fade-in {
423 animation: fade-in 750ms linear;
424 }
425 body.chat .anim-fade-in-fast {
426 animation: fade-in 350ms linear;
427 }
428 @keyframes fade-in {
429 from { opacity: 0; }
430 to { opacity: 1; }
431 }
432 body.chat .anim-fade-out-fast {
433 animation: fade-out 250ms linear;
434 }
435 @keyframes fade-out {
436 from { opacity: 1; }
437 to { opacity: 0; }
438 }
439

Keyboard Shortcuts

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