Fossil

Check-in [10107e4fbc]
Login

Check-in [10107e4fbc]

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: 10107e4fbc80d0b87dc0fae3a669d7d6d1b5eb5990f20553ed769129f69f9dac
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
Unified Diff Ignore Whitespace Patch
Changes to src/chat.c.
180
181
182
183
184
185
186
187
188
189
190
191
192


193

194

195
196
197
198
199
200
201
  @       </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'>
  @   <legend>Active users (sorted by last message time)</legend>
  @   <div id='chat-user-list'>
  @     <div class='help-buttonlet'>
  @      Users who have messages in the currently-loaded list.<br>
  @      Tap a user name to filter messages on that user and
  @      tap again to clear the filter.


  @     </div>

  @   </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'>







<
|
|
|
|
|
>
>
|
>

>







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
452


453
454
455
456
457
458
459
         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');
        });
        return this.e.currentView = D.removeClass(e,'hidden');


      },
      /**
         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(){







|
>
>







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

533
534
535
536
537
538
539
540
541
542
543
544
        });
        return this;
      },

      /**
         If animations are enabled, passes its arguments
         to D.addClassBriefly(), else this is a no-op.

         Returns this object;
      */
      animate: function f(e,a){
        if(!f.$disabled){
          D.addClassBriefly(e, a);
        }
        return this;
      }
    };
    cs.animate.$disabled = true;
    F.fetch.beforesend = ()=>cs.ajaxStart();
    F.fetch.aftersend = ()=>cs.ajaxEnd();







>
|

|

|







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
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
                      D.button(
                        "Message in context",
                        function(){
                          self.hide();
                          Chat.setUserFilter(false);
                          eMsg.scrollIntoView(false);
                          Chat.animate(
                            eMsg.firstElementChild, 'anim-rotate-360'
                            //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');

            }/*refresh()*/,
            hide: function(){
              D.addClass(D.clearElement(this.e), 'hidden');
              delete this.$eMsg;


            },
            show: function(tgtMsg){
              if(tgtMsg === this.$eMsg){
                this.hide();
                return;
              }
              this.$eMsg = tgtMsg;







|










<



>


<

>
>







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
1246
1247
1248
1249
1250













1251
1252
1253
1254
1255
1256
1257
    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();
            Chat.animate(Chat.e.activeUserListWrapper, "anim-flip-v");
          }
        }
      }
    };













    /* 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,







>









|




>
>
>
>
>
>
>
>
>
>
>
>
>







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
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.25em;
  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;
}







|







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
745
746
747
748
749
750
751
752
     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;
    }
    if(!e.forEach) e = [e];
    this.addClass(e, className);
    setTimeout(function(){
      dom.removeClass(e, className);
      if(afterCallback) afterCallback();
    }, howLongMs);
    return this;
  };







>
>
>










<







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
17
18
19
20
21
22
23
24
/* 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
     most 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. */













>
>
>
>



|







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
142
143
144
145
146
147
148
149
  z-index: 200;
}

/** Container for the list of /chat messages. */
body.chat #chat-messages-wrapper {
  overflow: auto;
  padding: 0 0.25em;
  margin-bottom: 0.5em;
}
body.chat #chat-messages-wrapper.loading > * {
  /* An attempt at reducing flicker when loading lots of messages. */
  visibility: hidden;
}
body.chat div.content {
  margin: 0;







<







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
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
  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: 0.55em 0 0.2em 0;
  padding: 0.25em 0.5em;
  font-size: 85%;
  border-style: inset;
  border-width: 0 1px 1px 1px/*else collides with the LEGEND*/;
}
body.chat #chat-user-list-wrapper > legend {




  font-weight: initial;
  padding: 0 0.5em;
  position: relative;
  top: -1.75ex;







  opacity: 0.6;













}
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 .help-buttonlet {
  margin: 0.2em 0.5em 0.2em 0;
}
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;







|
|
<



|
>
>
>
>

|

|
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>








<
<
<







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
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403








404
405
406


407




  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);
  }


}











<
|
<
<
|
<





<
|
<
<
|
<





|
|
|
>
>
>
>
>
>
>
>
|
<
|
>
>

>
>
>
>
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; }
}