Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Several minor cleanups, fixes, and presentation tweaks. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | chat-user-filter |
| Files: | files | file ages | folders |
| SHA3-256: |
10107e4fbc80d0b87dc0fae3a669d7d6 |
| User & Date: | stephan 2021-09-24 17:01:29.347 |
Context
|
2021-09-24
| ||
| 17:07 | One "last" style tweak, then i'm done. ... (check-in: 9e5acae7d5 user: stephan tags: chat-user-filter) | |
| 17:01 | Several minor cleanups, fixes, and presentation tweaks. ... (check-in: 10107e4fbc user: stephan tags: chat-user-filter) | |
| 13:20 | Minor doc corrections and cleanups. ... (check-in: a2588c570e user: stephan tags: chat-user-filter) | |
Changes
Changes to src/chat.c.
| ︙ | ︙ | |||
180 181 182 183 184 185 186 | @ </div> @ <input type="file" name="file" id="chat-input-file"> @ </div> @ <div id="chat-drop-details"></div> @ </div> @ </div> @ <div id='chat-user-list-wrapper' class='hidden'> | < | | | | | > > | > > | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | @ </div> @ <input type="file" name="file" id="chat-input-file"> @ </div> @ <div id="chat-drop-details"></div> @ </div> @ </div> @ <div id='chat-user-list-wrapper' class='hidden'> @ <div class='legend'> @ <span class='help-buttonlet'> @ Users who have messages in the currently-loaded list.<br><br> @ <strong>Tap a user name</strong> to filter messages @ on that user and tap again to clear the filter.<br><br> @ <strong>Tap the title</strong> of this widget to toggle @ the list on and off. @ </span> @ <span>Active users (sorted by last message time)</span> @ </div> @ <div id='chat-user-list'></div> @ </div> @ <div id='chat-preview' class='hidden chat-view'> @ <header>Preview: (<a href='%R/md_rules' target='_blank'>markdown reference</a>)</header> @ <div id='chat-preview-content' class='message-widget-content'></div> @ <div id='chat-preview-buttons'><button id='chat-preview-close'>Close Preview</button></div> @ </div> @ <div id='chat-config' class='hidden chat-view'> |
| ︙ | ︙ |
Changes to src/chat.js.
| ︙ | ︙ | |||
445 446 447 448 449 450 451 |
The 'hidden' class is removed from e and added to
all other elements in that list. Returns e.
*/
setCurrentView: function(e){
this.e.views.forEach(function(E){
if(e!==E) D.addClass(E,'hidden');
});
| | > > | 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 |
The 'hidden' class is removed from e and added to
all other elements in that list. Returns e.
*/
setCurrentView: function(e){
this.e.views.forEach(function(E){
if(e!==E) D.addClass(E,'hidden');
});
this.e.currentView = D.removeClass(e,'hidden');
this.animate(this.e.currentView, 'anim-fade-in-fast');
return this.e.currentView;
},
/**
Updates the "active user list" view if we are not currently
batch-loading messages and if the active user list UI element
is active.
*/
updateActiveUserList: function callee(){
|
| ︙ | ︙ | |||
526 527 528 529 530 531 532 |
});
return this;
},
/**
If animations are enabled, passes its arguments
to D.addClassBriefly(), else this is a no-op.
| > | | | | 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 |
});
return this;
},
/**
If animations are enabled, passes its arguments
to D.addClassBriefly(), else this is a no-op.
If cb is a function, it is called after the
CSS class is removed. Returns this object;
*/
animate: function f(e,a,cb){
if(!f.$disabled){
D.addClassBriefly(e, a, 0, cb);
}
return this;
}
};
cs.animate.$disabled = true;
F.fetch.beforesend = ()=>cs.ajaxStart();
F.fetch.aftersend = ()=>cs.ajaxEnd();
|
| ︙ | ︙ | |||
1006 1007 1008 1009 1010 1011 1012 |
D.button(
"Message in context",
function(){
self.hide();
Chat.setUserFilter(false);
eMsg.scrollIntoView(false);
Chat.animate(
| | < > < > > | 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 |
D.button(
"Message in context",
function(){
self.hide();
Chat.setUserFilter(false);
eMsg.scrollIntoView(false);
Chat.animate(
eMsg.firstElementChild, 'anim-flip-h'
//eMsg.firstElementChild, 'anim-flip-v'
//eMsg.childNodes, 'anim-rotate-360'
//eMsg.childNodes, 'anim-flip-v'
//eMsg, 'anim-flip-v'
);
})
)
);
}/*jump-to button*/
}
const tab = eMsg.querySelector('.message-widget-tab');
D.append(tab, this.e);
D.removeClass(this.e, 'hidden');
Chat.animate(this.e, 'anim-fade-in-fast');
}/*refresh()*/,
hide: function(){
delete this.$eMsg;
D.addClass(this.e, 'hidden');
D.clearElement(this.e);
},
show: function(tgtMsg){
if(tgtMsg === this.$eMsg){
this.hide();
return;
}
this.$eMsg = tgtMsg;
|
| ︙ | ︙ | |||
1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 |
const namedOptions = {
activeUsers:{
label: "Show active users list",
boolValue: ()=>!Chat.e.activeUserListWrapper.classList.contains('hidden'),
persistentSetting: 'active-user-list',
callback: function(){
D.toggleClass(Chat.e.activeUserListWrapper,'hidden');
if(Chat.e.activeUserListWrapper.classList.contains('hidden')){
/* When hiding this element, undo all filtering */
Chat.setUserFilter(false);
/*Ideally we'd scroll the final message into view
now, but because viewMessages is currently hidden behind
viewConfig, scrolling is a no-op. */
Chat.scrollMessagesTo(1);
}else{
Chat.updateActiveUserList();
| > | > > > > > > > > > > > > > | 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 |
const namedOptions = {
activeUsers:{
label: "Show active users list",
boolValue: ()=>!Chat.e.activeUserListWrapper.classList.contains('hidden'),
persistentSetting: 'active-user-list',
callback: function(){
D.toggleClass(Chat.e.activeUserListWrapper,'hidden');
D.removeClass(Chat.e.activeUserListWrapper, 'collapsed');
if(Chat.e.activeUserListWrapper.classList.contains('hidden')){
/* When hiding this element, undo all filtering */
Chat.setUserFilter(false);
/*Ideally we'd scroll the final message into view
now, but because viewMessages is currently hidden behind
viewConfig, scrolling is a no-op. */
Chat.scrollMessagesTo(1);
}else{
Chat.updateActiveUserList();
Chat.animate(Chat.e.activeUserListWrapper, 'anim-flip-v');
}
}
}
};
if(1){
/* Per user request, toggle the list of users on and off if the
legend element is tapped. */
const optAu = namedOptions.activeUsers;
optAu.theLegend = Chat.e.activeUserListWrapper.firstElementChild/*LEGEND*/;
optAu.theList = optAu.theLegend.nextElementSibling/*user list container*/;
optAu.theLegend.addEventListener('click',function(){
D.toggleClass(Chat.e.activeUserListWrapper, 'collapsed');
if(!Chat.e.activeUserListWrapper.classList.contains('collapsed')){
Chat.animate(optAu.theList,'anim-flip-v');
}
}, false);
}/*namedOptions.activeUsers additional setup*/
/* Settings menu entries... Remember that they will be rendered in
reverse order and the most frequently-needed ones "should"
(arguably) be closer to the start of this list so that they
will be rendered within easier reach of the settings button. */
const settingsOps = [{
label: "Multi-line input",
boolValue: ()=>Chat.inputElement()===Chat.e.inputMulti,
|
| ︙ | ︙ | |||
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 |
/* If the timestamp option is activated but
namedOptions.activeUsers is not currently checked then
toggle that option on as well. */
if(Chat.e.activeUserList.classList.contains('timestamps')
&& !namedOptions.activeUsers.boolValue()){
namedOptions.activeUsers.checkbox.checked = true;
namedOptions.activeUsers.callback();
}
}
},
namedOptions.activeUsers,{
label: "Monospace message font",
boolValue: ()=>document.body.classList.contains('monospace-messages'),
persistentSetting: 'monospace-messages',
| > | 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 |
/* If the timestamp option is activated but
namedOptions.activeUsers is not currently checked then
toggle that option on as well. */
if(Chat.e.activeUserList.classList.contains('timestamps')
&& !namedOptions.activeUsers.boolValue()){
namedOptions.activeUsers.checkbox.checked = true;
namedOptions.activeUsers.callback();
Chat.settings.set(namedOptions.activeUsers.persistentSetting, true);
}
}
},
namedOptions.activeUsers,{
label: "Monospace message font",
boolValue: ()=>document.body.classList.contains('monospace-messages'),
persistentSetting: 'monospace-messages',
|
| ︙ | ︙ |
Changes to src/default.css.
| ︙ | ︙ | |||
1385 1386 1387 1388 1389 1390 1391 |
margin-top: -2px/*restore alignment*/;
}
.fossil-tooltip {
text-align: center;
padding: 0.2em 1em;
border: 1px solid black;
| | | 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 |
margin-top: -2px/*restore alignment*/;
}
.fossil-tooltip {
text-align: center;
padding: 0.2em 1em;
border: 1px solid black;
border-radius: 0.5em;
position: absolute;
display: inline-block;
z-index: 19/*below default skin's hamburger popup*/;
box-shadow: -0.15em 0.15em 0.2em rgba(0, 0, 0, 0.75);
background-color: inherit;
color: inherit;
}
|
| ︙ | ︙ |
Changes to src/fossil.dom.js.
| ︙ | ︙ | |||
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 |
element or forEach-capable list of elements for howLongMs, then
removes it. If afterCallack is a function, it is called after the
CSS class is removed from all elements. If called with 3
arguments and the 3rd is a function, the 3rd is treated as a
callback and the default time (addClassBriefly.defaultTimeMs) is
used. If called with only 2 arguments, a time of
addClassBriefly.defaultTimeMs is used.
Returns this object but the CSS removal is asynchronous.
*/
dom.addClassBriefly = function f(e, className, howLongMs, afterCallback){
if(arguments.length<4 && 'function'===typeof howLongMs){
afterCallback = howLongMs;
howLongMs = f.defaultTimeMs;
}else if(arguments.length<3 || !+howLongMs){
howLongMs = f.defaultTimeMs;
}
| > > > < | 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 |
element or forEach-capable list of elements for howLongMs, then
removes it. If afterCallack is a function, it is called after the
CSS class is removed from all elements. If called with 3
arguments and the 3rd is a function, the 3rd is treated as a
callback and the default time (addClassBriefly.defaultTimeMs) is
used. If called with only 2 arguments, a time of
addClassBriefly.defaultTimeMs is used.
Passing a value of 0 for howLongMs causes the default value
to be applied.
Returns this object but the CSS removal is asynchronous.
*/
dom.addClassBriefly = function f(e, className, howLongMs, afterCallback){
if(arguments.length<4 && 'function'===typeof howLongMs){
afterCallback = howLongMs;
howLongMs = f.defaultTimeMs;
}else if(arguments.length<3 || !+howLongMs){
howLongMs = f.defaultTimeMs;
}
this.addClass(e, className);
setTimeout(function(){
dom.removeClass(e, className);
if(afterCallback) afterCallback();
}, howLongMs);
return this;
};
|
| ︙ | ︙ |
Changes to src/fossil.popupwidget.js.
| ︙ | ︙ | |||
353 354 355 356 357 358 359 360 361 362 363 364 365 366 |
when the botton is clicked.
*/
setup: function f(){
if(!f.hasOwnProperty('clickHandler')){
f.clickHandler = function fch(ev){
ev.preventDefault();
if(!fch.popup){
fch.popup = new F.PopupWidget({
cssClass: ['fossil-tooltip', 'help-buttonlet-content'],
refresh: function(){
}
});
fch.popup.e.style.maxWidth = '80%'/*of body*/;
| > | 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
when the botton is clicked.
*/
setup: function f(){
if(!f.hasOwnProperty('clickHandler')){
f.clickHandler = function fch(ev){
ev.preventDefault();
ev.stopPropagation();
if(!fch.popup){
fch.popup = new F.PopupWidget({
cssClass: ['fossil-tooltip', 'help-buttonlet-content'],
refresh: function(){
}
});
fch.popup.e.style.maxWidth = '80%'/*of body*/;
|
| ︙ | ︙ | |||
409 410 411 412 413 414 415 416 417 418 419 420 421 422 |
}else{
fch.popup.e.style.removeProperty('min-width');
x -= popupRect.width/2;
}
if(x<0) x = 0;
//console.debug("dimensions",x,y, popupRect, rectBody);
fch.popup.show(x, y);
};
f.foreachElement = function(e){
if(e.classList.contains('processed')) return;
e.classList.add('processed');
e.$helpContent = [];
/* We have to move all child nodes out of the way because we
cannot hide TEXT nodes via CSS (which cannot select TEXT
| > | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 |
}else{
fch.popup.e.style.removeProperty('min-width');
x -= popupRect.width/2;
}
if(x<0) x = 0;
//console.debug("dimensions",x,y, popupRect, rectBody);
fch.popup.show(x, y);
return false;
};
f.foreachElement = function(e){
if(e.classList.contains('processed')) return;
e.classList.add('processed');
e.$helpContent = [];
/* We have to move all child nodes out of the way because we
cannot hide TEXT nodes via CSS (which cannot select TEXT
|
| ︙ | ︙ |
Changes to src/style.chat.css.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* Chat-related */
body.chat span.at-name { /* for @USERNAME references */
text-decoration: underline;
font-weight: bold;
}
/* A wrapper for a single single chat message (one row of the UI) */
body.chat .message-widget {
margin-bottom: 0.75em;
border: none;
display: flex;
flex-direction: column;
border: none;
align-items: flex-start;
}
body.chat.my-messages-right .message-widget.mine {
/* Right-aligns a user's own chat messages, similar to how
| > > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
/* Chat-related */
body.chat span.at-name { /* for @USERNAME references */
text-decoration: underline;
font-weight: bold;
}
/* A wrapper for a single single chat message (one row of the UI) */
body.chat .message-widget {
margin-bottom: 0.75em;
border: none;
display: flex;
flex-direction: column;
border: none;
align-items: flex-start;
}
body.chat .message-widget:last-of-type {
/* Latest message: reduce bottom gap */
margin-bottom: 0.1em;
}
body.chat.my-messages-right .message-widget.mine {
/* Right-aligns a user's own chat messages, similar to how
most/some mobile messaging apps do it. */
align-items: flex-end;
}
body.chat.my-messages-right .message-widget.notification {
/* Center-aligns a system-level notification message. */
align-items: center;
}
/* The content area of a message. */
|
| ︙ | ︙ | |||
75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
font-size: 0.9em;
text-align: left;
display: flex;
flex-direction: column;
align-items: stretch;
padding: 0.25em;
margin-top: 0.25em;
}
/* Full message timestamps. */
body.chat .chat-message-popup > span { white-space: nowrap; }
/* Container for the message deletion buttons. */
body.chat .chat-message-popup > .toolbar {
padding: 0;
margin: 0;
| > > | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
font-size: 0.9em;
text-align: left;
display: flex;
flex-direction: column;
align-items: stretch;
padding: 0.25em;
margin-top: 0.25em;
border: 1px outset;
border-radius: 0.5em;
}
/* Full message timestamps. */
body.chat .chat-message-popup > span { white-space: nowrap; }
/* Container for the message deletion buttons. */
body.chat .chat-message-popup > .toolbar {
padding: 0;
margin: 0;
|
| ︙ | ︙ | |||
135 136 137 138 139 140 141 |
z-index: 200;
}
/** Container for the list of /chat messages. */
body.chat #chat-messages-wrapper {
overflow: auto;
padding: 0 0.25em;
| < | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
z-index: 200;
}
/** Container for the list of /chat messages. */
body.chat #chat-messages-wrapper {
overflow: auto;
padding: 0 0.25em;
}
body.chat #chat-messages-wrapper.loading > * {
/* An attempt at reducing flicker when loading lots of messages. */
visibility: hidden;
}
body.chat div.content {
margin: 0;
|
| ︙ | ︙ | |||
265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
body.chat #chat-drop-details img {
max-width: 45%;
max-height: 45%;
}
body.chat .chat-view {
flex: 20 1 auto
/*ensure that these grow more than the non-.chat-view elements*/;
}
body.chat #chat-config,
body.chat #chat-preview {
/* /chat configuration widget */
display: flex;
flex-direction: column;
overflow: auto;
| > | 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
body.chat #chat-drop-details img {
max-width: 45%;
max-height: 45%;
}
body.chat .chat-view {
flex: 20 1 auto
/*ensure that these grow more than the non-.chat-view elements*/;
margin-bottom: 0.2em;
}
body.chat #chat-config,
body.chat #chat-preview {
/* /chat configuration widget */
display: flex;
flex-direction: column;
overflow: auto;
|
| ︙ | ︙ | |||
325 326 327 328 329 330 331 |
flex: 0 1 auto;
margin: 0.25em 0;
}
body.chat #chat-user-list-wrapper {
/* Safari can't do fieldsets right, so we emulate one. */
border-radius: 0.5em;
| | | < | > > > > | | > > > > > > > > > > > > > > > > > > > > < < < | 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
flex: 0 1 auto;
margin: 0.25em 0;
}
body.chat #chat-user-list-wrapper {
/* Safari can't do fieldsets right, so we emulate one. */
border-radius: 0.5em;
margin: 1em 0 0.2em 0;
padding: 0 0.5em;
border-style: inset;
border-width: 0 1px 1px 1px/*else collides with the LEGEND*/;
}
body.chat #chat-user-list-wrapper.collapsed {
padding: 0;
margin-bottom: 0;
}
body.chat #chat-user-list-wrapper > .legend {
font-weight: initial;
padding: 0 0.5em 0 0.5em;
position: relative;
top: -1.75ex/* place it like a fieldset legend */;
cursor: pointer;
}
body.chat #chat-user-list-wrapper > .legend > * {
vertical-align: middle;
}
body.chat #chat-user-list-wrapper > .legend > *:nth-child(2){
/* Title label */
opacity: 0.6;
font-size: 0.8em;
}
body.chat #chat-user-list-wrapper.collapsed > .legend > *:nth-child(2)::after {
content: " (tap to toggle)";
}
body.chat #chat-user-list-wrapper .help-buttonlet {
margin: 0;
}
body.chat #chat-user-list-wrapper.collapsed #chat-user-list {
position: absolute !important;
opacity: 0 !important;
pointer-events: none !important;
display: none !important;
}
body.chat #chat-user-list {
margin-top: -1.25ex;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
}
body.chat #chat-user-list .chat-user {
margin: 0.2em;
padding: 0.1em 0.5em 0.2em 0.5em;
border-radius: 0.5em;
cursor: pointer;
text-align: center;
white-space: pre;
|
| ︙ | ︙ | |||
372 373 374 375 376 377 378 |
text-decoration: underline;
}
body.chat .anim-rotate-360 {
animation: rotate-360 750ms linear;
}
@keyframes rotate-360 {
| < | < < | < < | < < | < | | | > > > > > > > > | < | > > > > > > | 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
text-decoration: underline;
}
body.chat .anim-rotate-360 {
animation: rotate-360 750ms linear;
}
@keyframes rotate-360 {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
body.chat .anim-flip-h {
animation: flip-h 750ms linear;
}
@keyframes flip-h{
from { transform: rotateY(0deg); }
to { transform: rotateY(360deg); }
}
body.chat .anim-flip-v {
animation: flip-v 750ms linear;
}
@keyframes flip-v{
from { transform: rotateX(0deg); }
to { transform: rotateX(360deg); }
}
body.chat .anim-fade-in {
animation: fade-in 750ms linear;
}
body.chat .anim-fade-in-fast {
animation: fade-in 350ms linear;
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
body.chat .anim-fade-out-fast {
animation: fade-out 250ms linear;
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
|