Fossil

Check-in [299fd6905f]
Login

Check-in [299fd6905f]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Refactored the calc-elem-effective-height routine into the fossil.dom API for reuse elsewhere. Fixed (arguably) a minor layout quirk in the chat input field in multi-line mode.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 299fd6905f52c9c24898dc5121e63bed054280b3ba7bc5bb1648be42b5ddec29
User & Date: stephan 2020-12-27 21:22:26.035
Context
2020-12-27
22:01
chat: when toggling between single/multi-line mode, retain the message area scroll position, insofar as its size allows for (e.g. might not work if the history is too short to scroll). ... (check-in: 423ee8101a user: stephan tags: trunk)
21:22
Refactored the calc-elem-effective-height routine into the fossil.dom API for reuse elsewhere. Fixed (arguably) a minor layout quirk in the chat input field in multi-line mode. ... (check-in: 299fd6905f user: stephan tags: trunk)
21:00
Put the <form> outside of the chat-input-area <div>. Safari requires this. ... (check-in: ca60df9238 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/chat.js.
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

85
86
87
88
89
90
91
92
93
94
95



96
97
98
99
100
101
102
103
104
105
      f.elemsToCount = [
        document.querySelector('body > div.header'),
        document.querySelector('body > div.mainmenu'),
        document.querySelector('body > #hbdrop'),
        document.querySelector('body > div.footer')
      ];
      f.contentArea = E1('div.content');
      f.extra = 0;
      f.measure = function callee(e,depth){
        if(!e) return;
        const m = e.getBoundingClientRect();
        //console.debug("level-"+depth+" rect",e.className,m.top,m.bottom);
        if(0===depth){
          callee.top = m.top;
          callee.bottom = m.bottom;
        }else{
          callee.top = m.top ? Math.min(callee.top, m.top) : callee.top;
          callee.bottom = Math.max(callee.bottom, m.bottom);
        }
        Array.prototype.forEach.call(e.children,(e)=>callee(e,depth+1));
        if(0===depth){
          //console.debug("measure() height:",e.className, callee.top, callee.bottom, (callee.bottom - callee.top));
          f.extra += callee.bottom - callee.top;
        }
      };
    }
    const bcl = document.body.classList;
    const resized = function(){
      const wh = window.innerHeight,
            com = bcl.contains('chat-only-mode');
      var ht;

      if(com){
        ht = wh;
      }else{
        f.extra = 0;
        f.elemsToCount.forEach((e)=>e ? f.measure(e,0) : false);
        ht = wh - f.extra;
      }
      f.contentArea.style.height =
        f.contentArea.style.maxHeight = [
          "calc(", (ht>=100 ? ht : 100), "px",
          " - 1em"/*fudge value*/,")"



        ].join('');
      if(false){
        console.debug("resized.",wh, f.extra, ht,
                      window.getComputedStyle(f.contentArea).maxHeight,
                      f.contentArea);
      }
    };
    var doit;
    window.addEventListener('resize',function(ev){
      clearTimeout(doit);







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






>



<
|
|





>
>
>


|







54
55
56
57
58
59
60


















61
62
63
64
65
66
67
68
69
70

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
      f.elemsToCount = [
        document.querySelector('body > div.header'),
        document.querySelector('body > div.mainmenu'),
        document.querySelector('body > #hbdrop'),
        document.querySelector('body > div.footer')
      ];
      f.contentArea = E1('div.content');


















    }
    const bcl = document.body.classList;
    const resized = function(){
      const wh = window.innerHeight,
            com = bcl.contains('chat-only-mode');
      var ht;
      var extra = 0;
      if(com){
        ht = wh;
      }else{

        f.elemsToCount.forEach((e)=>e ? extra += D.effectiveHeight(e) : false);
        ht = wh - extra;
      }
      f.contentArea.style.height =
        f.contentArea.style.maxHeight = [
          "calc(", (ht>=100 ? ht : 100), "px",
          " - 1em"/*fudge value*/,")"
          /* ^^^^ hypothetically not needed, but both Chrome/FF on
             Linux will force scrollbars on the body if this value is
             too small (<0.75em in my tests). */
        ].join('');
      if(false){
        console.debug("resized.",wh, extra, ht,
                      window.getComputedStyle(f.contentArea).maxHeight,
                      f.contentArea);
      }
    };
    var doit;
    window.addEventListener('resize',function(ev){
      clearTimeout(doit);
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
        }else{
          D.append(holder,e);
          this.e.newestMessage = e;
        }
        if(!atEnd && !this.isBatchLoading
           && e.dataset.xfrom!==this.me
           && (prevMessage
               ? !overlapsElemView(prevMessage, this.e.messagesWrapper)
               : false)){
          /* If a new non-history message arrives while the user is
             scrolled elsewhere, do not scroll to the latest
             message, but gently alert the user that a new message
             has arrived. */
          F.toast.message("New message has arrived.");
        }else if(!this.isBatchLoading && e.dataset.xfrom===Chat.me){







|







227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
        }else{
          D.append(holder,e);
          this.e.newestMessage = e;
        }
        if(!atEnd && !this.isBatchLoading
           && e.dataset.xfrom!==this.me
           && (prevMessage
               ? !this.messageIsInView(prevMessage)
               : false)){
          /* If a new non-history message arrives while the user is
             scrolled elsewhere, do not scroll to the latest
             message, but gently alert the user that a new message
             has arrived. */
          F.toast.message("New message has arrived.");
        }else if(!this.isBatchLoading && e.dataset.xfrom===Chat.me){
339
340
341
342
343
344
345



346
347
348
349
350
351
352
         (yes==false), or toggle them (no arguments). Returns this. */
      toggleNavButtons: function(yes){
        const e = [this.e.btnMsgHome, this.e.btnMsgEnd], c = 'hidden';
        if(0===arguments.length) D.toggleClass(e, c);
        else if(!arguments[0]) D.addClass(e, c);
        else D.removeClass(e, c);
      },



      settings:{
        get: (k,dflt)=>F.storage.get(k,dflt),
        getBool: (k,dflt)=>F.storage.getBool(k,dflt),
        set: (k,v)=>F.storage.set(k,v),
        defaults:{
          "images-inline": !!F.config.chat.imagesInline,
          "monospace-messages": false







>
>
>







324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
         (yes==false), or toggle them (no arguments). Returns this. */
      toggleNavButtons: function(yes){
        const e = [this.e.btnMsgHome, this.e.btnMsgEnd], c = 'hidden';
        if(0===arguments.length) D.toggleClass(e, c);
        else if(!arguments[0]) D.addClass(e, c);
        else D.removeClass(e, c);
      },
      messageIsInView: function(e){
        return e ? overlapsElemView(e, this.e.messagesWrapper) : false;
      },
      settings:{
        get: (k,dflt)=>F.storage.get(k,dflt),
        getBool: (k,dflt)=>F.storage.getBool(k,dflt),
        set: (k,v)=>F.storage.set(k,v),
        defaults:{
          "images-inline": !!F.config.chat.imagesInline,
          "monospace-messages": false
Changes to src/default.css.
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696

/* Widget holding the chat message input field, send button, and
   settings button. */
body.chat #chat-input-line {
  display: flex;
  flex-direction: row;
  margin-bottom: 0.25em;
  align-items: center;
}
body.chat #chat-input-line > input[type=submit],
body.chat #chat-input-line > #chat-settings-button,
body.chat #chat-input-line > button {
  flex: 1 5 auto;
  max-width: 6em;
  margin: 0 0.25em;







|







1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696

/* Widget holding the chat message input field, send button, and
   settings button. */
body.chat #chat-input-line {
  display: flex;
  flex-direction: row;
  margin-bottom: 0.25em;
  align-items: self-start;
}
body.chat #chat-input-line > input[type=submit],
body.chat #chat-input-line > #chat-settings-button,
body.chat #chat-input-line > button {
  flex: 1 5 auto;
  max-width: 6em;
  margin: 0 0.25em;
Changes to src/fossil.dom.js.
721
722
723
724
725
726
727



































728
729
730
731
732
733
734
      let k;
      for(k in style){
        if(style.hasOwnProperty(k)) e.style[k] = style[k];
      }
    }
    return e;
  };




































  /**
     Parses a string as HTML.

     Usages:

     Array (htmlString)







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







721
722
723
724
725
726
727
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
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
      let k;
      for(k in style){
        if(style.hasOwnProperty(k)) e.style[k] = style[k];
      }
    }
    return e;
  };

  /**
     Given a DOM element, this routine measures its "effective
     height", which is the bounding top/bottom range of this element
     and all of its children, recursively. For some DOM structure
     cases, a parent may have a reported height of 0 even though
     children have non-0 sizes.

     Returns 0 if !e or if the element really has no height.
  */
  dom.effectiveHeight = function f(e){
    if(!e) return 0;
    if(!f.measure){
      f.measure = function callee(e, depth){
        if(!e) return;
        const m = e.getBoundingClientRect();
        if(0===depth){
          callee.top = m.top;
          callee.bottom = m.bottom;
        }else{
          callee.top = m.top ? Math.min(callee.top, m.top) : callee.top;
          callee.bottom = Math.max(callee.bottom, m.bottom);
        }
        Array.prototype.forEach.call(e.children,(e)=>callee(e,depth+1));
        if(0===depth){
          //console.debug("measure() height:",e.className, callee.top, callee.bottom, (callee.bottom - callee.top));
          f.extra += callee.bottom - callee.top;
        }
        return f.extra;
      };
    }
    f.extra = 0;
    f.measure(e,0);
    return f.extra;
  };

  /**
     Parses a string as HTML.

     Usages:

     Array (htmlString)