Fossil SCM

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.

stephan 2020-12-27 21:22 trunk
Commit 299fd6905f52c9c24898dc5121e63bed054280b3ba7bc5bb1648be42b5ddec29
+11 -23
--- src/chat.js
+++ src/chat.js
@@ -56,48 +56,33 @@
5656
document.querySelector('body > div.mainmenu'),
5757
document.querySelector('body > #hbdrop'),
5858
document.querySelector('body > div.footer')
5959
];
6060
f.contentArea = E1('div.content');
61
- f.extra = 0;
62
- f.measure = function callee(e,depth){
63
- if(!e) return;
64
- const m = e.getBoundingClientRect();
65
- //console.debug("level-"+depth+" rect",e.className,m.top,m.bottom);
66
- if(0===depth){
67
- callee.top = m.top;
68
- callee.bottom = m.bottom;
69
- }else{
70
- callee.top = m.top ? Math.min(callee.top, m.top) : callee.top;
71
- callee.bottom = Math.max(callee.bottom, m.bottom);
72
- }
73
- Array.prototype.forEach.call(e.children,(e)=>callee(e,depth+1));
74
- if(0===depth){
75
- //console.debug("measure() height:",e.className, callee.top, callee.bottom, (callee.bottom - callee.top));
76
- f.extra += callee.bottom - callee.top;
77
- }
78
- };
7961
}
8062
const bcl = document.body.classList;
8163
const resized = function(){
8264
const wh = window.innerHeight,
8365
com = bcl.contains('chat-only-mode');
8466
var ht;
67
+ var extra = 0;
8568
if(com){
8669
ht = wh;
8770
}else{
88
- f.extra = 0;
89
- f.elemsToCount.forEach((e)=>e ? f.measure(e,0) : false);
90
- ht = wh - f.extra;
71
+ f.elemsToCount.forEach((e)=>e ? extra += D.effectiveHeight(e) : false);
72
+ ht = wh - extra;
9173
}
9274
f.contentArea.style.height =
9375
f.contentArea.style.maxHeight = [
9476
"calc(", (ht>=100 ? ht : 100), "px",
9577
" - 1em"/*fudge value*/,")"
78
+ /* ^^^^ hypothetically not needed, but both Chrome/FF on
79
+ Linux will force scrollbars on the body if this value is
80
+ too small (<0.75em in my tests). */
9681
].join('');
9782
if(false){
98
- console.debug("resized.",wh, f.extra, ht,
83
+ console.debug("resized.",wh, extra, ht,
9984
window.getComputedStyle(f.contentArea).maxHeight,
10085
f.contentArea);
10186
}
10287
};
10388
var doit;
@@ -244,11 +229,11 @@
244229
this.e.newestMessage = e;
245230
}
246231
if(!atEnd && !this.isBatchLoading
247232
&& e.dataset.xfrom!==this.me
248233
&& (prevMessage
249
- ? !overlapsElemView(prevMessage, this.e.messagesWrapper)
234
+ ? !this.messageIsInView(prevMessage)
250235
: false)){
251236
/* If a new non-history message arrives while the user is
252237
scrolled elsewhere, do not scroll to the latest
253238
message, but gently alert the user that a new message
254239
has arrived. */
@@ -341,10 +326,13 @@
341326
const e = [this.e.btnMsgHome, this.e.btnMsgEnd], c = 'hidden';
342327
if(0===arguments.length) D.toggleClass(e, c);
343328
else if(!arguments[0]) D.addClass(e, c);
344329
else D.removeClass(e, c);
345330
},
331
+ messageIsInView: function(e){
332
+ return e ? overlapsElemView(e, this.e.messagesWrapper) : false;
333
+ },
346334
settings:{
347335
get: (k,dflt)=>F.storage.get(k,dflt),
348336
getBool: (k,dflt)=>F.storage.getBool(k,dflt),
349337
set: (k,v)=>F.storage.set(k,v),
350338
defaults:{
351339
--- src/chat.js
+++ src/chat.js
@@ -56,48 +56,33 @@
56 document.querySelector('body > div.mainmenu'),
57 document.querySelector('body > #hbdrop'),
58 document.querySelector('body > div.footer')
59 ];
60 f.contentArea = E1('div.content');
61 f.extra = 0;
62 f.measure = function callee(e,depth){
63 if(!e) return;
64 const m = e.getBoundingClientRect();
65 //console.debug("level-"+depth+" rect",e.className,m.top,m.bottom);
66 if(0===depth){
67 callee.top = m.top;
68 callee.bottom = m.bottom;
69 }else{
70 callee.top = m.top ? Math.min(callee.top, m.top) : callee.top;
71 callee.bottom = Math.max(callee.bottom, m.bottom);
72 }
73 Array.prototype.forEach.call(e.children,(e)=>callee(e,depth+1));
74 if(0===depth){
75 //console.debug("measure() height:",e.className, callee.top, callee.bottom, (callee.bottom - callee.top));
76 f.extra += callee.bottom - callee.top;
77 }
78 };
79 }
80 const bcl = document.body.classList;
81 const resized = function(){
82 const wh = window.innerHeight,
83 com = bcl.contains('chat-only-mode');
84 var ht;
 
85 if(com){
86 ht = wh;
87 }else{
88 f.extra = 0;
89 f.elemsToCount.forEach((e)=>e ? f.measure(e,0) : false);
90 ht = wh - f.extra;
91 }
92 f.contentArea.style.height =
93 f.contentArea.style.maxHeight = [
94 "calc(", (ht>=100 ? ht : 100), "px",
95 " - 1em"/*fudge value*/,")"
 
 
 
96 ].join('');
97 if(false){
98 console.debug("resized.",wh, f.extra, ht,
99 window.getComputedStyle(f.contentArea).maxHeight,
100 f.contentArea);
101 }
102 };
103 var doit;
@@ -244,11 +229,11 @@
244 this.e.newestMessage = e;
245 }
246 if(!atEnd && !this.isBatchLoading
247 && e.dataset.xfrom!==this.me
248 && (prevMessage
249 ? !overlapsElemView(prevMessage, this.e.messagesWrapper)
250 : false)){
251 /* If a new non-history message arrives while the user is
252 scrolled elsewhere, do not scroll to the latest
253 message, but gently alert the user that a new message
254 has arrived. */
@@ -341,10 +326,13 @@
341 const e = [this.e.btnMsgHome, this.e.btnMsgEnd], c = 'hidden';
342 if(0===arguments.length) D.toggleClass(e, c);
343 else if(!arguments[0]) D.addClass(e, c);
344 else D.removeClass(e, c);
345 },
 
 
 
346 settings:{
347 get: (k,dflt)=>F.storage.get(k,dflt),
348 getBool: (k,dflt)=>F.storage.getBool(k,dflt),
349 set: (k,v)=>F.storage.set(k,v),
350 defaults:{
351
--- src/chat.js
+++ src/chat.js
@@ -56,48 +56,33 @@
56 document.querySelector('body > div.mainmenu'),
57 document.querySelector('body > #hbdrop'),
58 document.querySelector('body > div.footer')
59 ];
60 f.contentArea = E1('div.content');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61 }
62 const bcl = document.body.classList;
63 const resized = function(){
64 const wh = window.innerHeight,
65 com = bcl.contains('chat-only-mode');
66 var ht;
67 var extra = 0;
68 if(com){
69 ht = wh;
70 }else{
71 f.elemsToCount.forEach((e)=>e ? extra += D.effectiveHeight(e) : false);
72 ht = wh - extra;
 
73 }
74 f.contentArea.style.height =
75 f.contentArea.style.maxHeight = [
76 "calc(", (ht>=100 ? ht : 100), "px",
77 " - 1em"/*fudge value*/,")"
78 /* ^^^^ hypothetically not needed, but both Chrome/FF on
79 Linux will force scrollbars on the body if this value is
80 too small (<0.75em in my tests). */
81 ].join('');
82 if(false){
83 console.debug("resized.",wh, extra, ht,
84 window.getComputedStyle(f.contentArea).maxHeight,
85 f.contentArea);
86 }
87 };
88 var doit;
@@ -244,11 +229,11 @@
229 this.e.newestMessage = e;
230 }
231 if(!atEnd && !this.isBatchLoading
232 && e.dataset.xfrom!==this.me
233 && (prevMessage
234 ? !this.messageIsInView(prevMessage)
235 : false)){
236 /* If a new non-history message arrives while the user is
237 scrolled elsewhere, do not scroll to the latest
238 message, but gently alert the user that a new message
239 has arrived. */
@@ -341,10 +326,13 @@
326 const e = [this.e.btnMsgHome, this.e.btnMsgEnd], c = 'hidden';
327 if(0===arguments.length) D.toggleClass(e, c);
328 else if(!arguments[0]) D.addClass(e, c);
329 else D.removeClass(e, c);
330 },
331 messageIsInView: function(e){
332 return e ? overlapsElemView(e, this.e.messagesWrapper) : false;
333 },
334 settings:{
335 get: (k,dflt)=>F.storage.get(k,dflt),
336 getBool: (k,dflt)=>F.storage.getBool(k,dflt),
337 set: (k,v)=>F.storage.set(k,v),
338 defaults:{
339
+1 -1
--- src/default.css
+++ src/default.css
@@ -1684,11 +1684,11 @@
16841684
settings button. */
16851685
body.chat #chat-input-line {
16861686
display: flex;
16871687
flex-direction: row;
16881688
margin-bottom: 0.25em;
1689
- align-items: center;
1689
+ align-items: self-start;
16901690
}
16911691
body.chat #chat-input-line > input[type=submit],
16921692
body.chat #chat-input-line > #chat-settings-button,
16931693
body.chat #chat-input-line > button {
16941694
flex: 1 5 auto;
16951695
--- src/default.css
+++ src/default.css
@@ -1684,11 +1684,11 @@
1684 settings button. */
1685 body.chat #chat-input-line {
1686 display: flex;
1687 flex-direction: row;
1688 margin-bottom: 0.25em;
1689 align-items: center;
1690 }
1691 body.chat #chat-input-line > input[type=submit],
1692 body.chat #chat-input-line > #chat-settings-button,
1693 body.chat #chat-input-line > button {
1694 flex: 1 5 auto;
1695
--- src/default.css
+++ src/default.css
@@ -1684,11 +1684,11 @@
1684 settings button. */
1685 body.chat #chat-input-line {
1686 display: flex;
1687 flex-direction: row;
1688 margin-bottom: 0.25em;
1689 align-items: self-start;
1690 }
1691 body.chat #chat-input-line > input[type=submit],
1692 body.chat #chat-input-line > #chat-settings-button,
1693 body.chat #chat-input-line > button {
1694 flex: 1 5 auto;
1695
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -723,10 +723,45 @@
723723
if(style.hasOwnProperty(k)) e.style[k] = style[k];
724724
}
725725
}
726726
return e;
727727
};
728
+
729
+ /**
730
+ Given a DOM element, this routine measures its "effective
731
+ height", which is the bounding top/bottom range of this element
732
+ and all of its children, recursively. For some DOM structure
733
+ cases, a parent may have a reported height of 0 even though
734
+ children have non-0 sizes.
735
+
736
+ Returns 0 if !e or if the element really has no height.
737
+ */
738
+ dom.effectiveHeight = function f(e){
739
+ if(!e) return 0;
740
+ if(!f.measure){
741
+ f.measure = function callee(e, depth){
742
+ if(!e) return;
743
+ const m = e.getBoundingClientRect();
744
+ if(0===depth){
745
+ callee.top = m.top;
746
+ callee.bottom = m.bottom;
747
+ }else{
748
+ callee.top = m.top ? Math.min(callee.top, m.top) : callee.top;
749
+ callee.bottom = Math.max(callee.bottom, m.bottom);
750
+ }
751
+ Array.prototype.forEach.call(e.children,(e)=>callee(e,depth+1));
752
+ if(0===depth){
753
+ //console.debug("measure() height:",e.className, callee.top, callee.bottom, (callee.bottom - callee.top));
754
+ f.extra += callee.bottom - callee.top;
755
+ }
756
+ return f.extra;
757
+ };
758
+ }
759
+ f.extra = 0;
760
+ f.measure(e,0);
761
+ return f.extra;
762
+ };
728763
729764
/**
730765
Parses a string as HTML.
731766
732767
Usages:
733768
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -723,10 +723,45 @@
723 if(style.hasOwnProperty(k)) e.style[k] = style[k];
724 }
725 }
726 return e;
727 };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
728
729 /**
730 Parses a string as HTML.
731
732 Usages:
733
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -723,10 +723,45 @@
723 if(style.hasOwnProperty(k)) e.style[k] = style[k];
724 }
725 }
726 return e;
727 };
728
729 /**
730 Given a DOM element, this routine measures its "effective
731 height", which is the bounding top/bottom range of this element
732 and all of its children, recursively. For some DOM structure
733 cases, a parent may have a reported height of 0 even though
734 children have non-0 sizes.
735
736 Returns 0 if !e or if the element really has no height.
737 */
738 dom.effectiveHeight = function f(e){
739 if(!e) return 0;
740 if(!f.measure){
741 f.measure = function callee(e, depth){
742 if(!e) return;
743 const m = e.getBoundingClientRect();
744 if(0===depth){
745 callee.top = m.top;
746 callee.bottom = m.bottom;
747 }else{
748 callee.top = m.top ? Math.min(callee.top, m.top) : callee.top;
749 callee.bottom = Math.max(callee.bottom, m.bottom);
750 }
751 Array.prototype.forEach.call(e.children,(e)=>callee(e,depth+1));
752 if(0===depth){
753 //console.debug("measure() height:",e.className, callee.top, callee.bottom, (callee.bottom - callee.top));
754 f.extra += callee.bottom - callee.top;
755 }
756 return f.extra;
757 };
758 }
759 f.extra = 0;
760 f.measure(e,0);
761 return f.extra;
762 };
763
764 /**
765 Parses a string as HTML.
766
767 Usages:
768

Keyboard Shortcuts

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