Fossil

Check-in [1d467dcb71]
Login

Check-in [1d467dcb71]

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

Overview
Comment:/forumthread: dynamically determine which posts have scrollbars (i.e. are taller than div.forumPostBody's max-height) and only add the expand/collapse toggle to those posts.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | forum-expand-poc
Files: files | file ages | folders
SHA3-256: 1d467dcb71edb7cefcf318b6a25031dacc7e965f57b15862ca0f34e5735a2931
User & Date: stephan 2020-07-09 17:36:00.906
Context
2020-07-09
18:06
For expanded view, use max-height:initial instead of some arbitrarily large value. That will effectively disable max-height. Change the pointer for the expand toggle to 'cursor'. ... (check-in: c824d1dc3e user: stephan tags: forum-expand-poc)
17:36
/forumthread: dynamically determine which posts have scrollbars (i.e. are taller than div.forumPostBody's max-height) and only add the expand/collapse toggle to those posts. ... (check-in: 1d467dcb71 user: stephan tags: forum-expand-poc)
15:59
Expand/collapse toggle is no longer 100% wide. ... (check-in: bb333b0bd9 user: stephan tags: forum-expand-poc)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/default.css.
767
768
769
770
771
772
773
774



775
776
777
778
779
780
781
  margin-top: 1ex;
}
div.forumHier, div.forumTime, div.forumHierRoot {
  display: flex;
  flex-direction: column;
}
div.forumPostBody {
  max-height: 100em;



  overflow: auto;
}
div.forumSel {
  background-color: #cef;
}
div.forumObs {
  color: #bbb;







|
>
>
>







767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
  margin-top: 1ex;
}
div.forumHier, div.forumTime, div.forumHierRoot {
  display: flex;
  flex-direction: column;
}
div.forumPostBody {
  max-height: 20em /* Posts which overflow this value get an
                      Expand/Collapse toggle injected at page-load.
                      It's currently intentionally set low for
                      demonstration purposes. */;
  overflow: auto;
}
div.forumSel {
  background-color: #cef;
}
div.forumObs {
  color: #bbb;
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
}
label.forum-post-collapser {
  align-self: flex-start;
  padding: 0.1em 0.5em;
  border: 1px outset;
  border-radius: 0.25em;
}
input[type=checkbox].forum-post-collapser:not(:checked) ~ div.forumPostBody {
  max-height: 10em;
}
input[type=checkbox].forum-post-collapser:checked ~ div.forumPostBody {
  max-height: 200em;
}

#capabilitySummary {
  text-align: center;
}
#capabilitySummary td {
  padding-left: 3ex;







<
<
<

|







797
798
799
800
801
802
803



804
805
806
807
808
809
810
811
812
}
label.forum-post-collapser {
  align-self: flex-start;
  padding: 0.1em 0.5em;
  border: 1px outset;
  border-radius: 0.25em;
}



input[type=checkbox].forum-post-collapser:checked ~ div.forumPostBody {
  max-height: 10000em /* some "absurdly large" value */;
}

#capabilitySummary {
  text-align: center;
}
#capabilitySummary td {
  padding-left: 3ex;
Changes to src/forum.c.
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
  }else{
    zResult = fossil_strdup(zLogin);
  }
  db_reset(&q);
  return zResult;
}

/*
** Emits a checkbox and label for implementing a CSS-only
** collapse/expand button on posts. It should be passed the UUID of
** the current post, but that value is only used for constructing a
** unique ID for the (invisible) checkbox so that the label can be
** bound to it via its 'for' attribute. Thus it doesn't really matter
** whether the UUID refers to the current (edited) instance of the
** post or an ancestor version, so long as the UUID is unique within
** the current page.
*/
static void forum_emit_post_toggle(const char * zUuid){
  @ <input type='checkbox' id='cb-post-%S(zUuid)' \
  @ class='forum-post-collapser'>
  @ <label for='cb-post-%S(zUuid)' \
  @ class='forum-post-collapser'></label>
}

/*
** Display all posts in a forum thread in chronological order
*/
static void forum_display_chronological(int froot, int target, int bRawMode){
  ForumThread *pThread = forumthread_create(froot, 0);
  ForumEntry *p;
  int notAnon = login_is_individual();







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







400
401
402
403
404
405
406

















407
408
409
410
411
412
413
  }else{
    zResult = fossil_strdup(zLogin);
  }
  db_reset(&q);
  return zResult;
}


















/*
** Display all posts in a forum thread in chronological order
*/
static void forum_display_chronological(int froot, int target, int bRawMode){
  ForumThread *pThread = forumthread_create(froot, 0);
  ForumEntry *p;
  int notAnon = login_is_individual();
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
    }
    if( !bRawMode ){
      @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a>
    }
    isPrivate = content_is_private(p->fpid);
    sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0;
    @ </h3>
    forum_emit_post_toggle(p->zUuid);
    if( isPrivate && !g.perm.ModForum && !sameUser ){
      @ <p><span class="modpending">Awaiting Moderator Approval</span></p>
    }else{
      const char *zMimetype;
      if( bRawMode ){
        zMimetype = "text/plain";
      }else if( p->pLeaf!=0 ){







<







466
467
468
469
470
471
472

473
474
475
476
477
478
479
    }
    if( !bRawMode ){
      @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a>
    }
    isPrivate = content_is_private(p->fpid);
    sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0;
    @ </h3>

    if( isPrivate && !g.perm.ModForum && !sameUser ){
      @ <p><span class="modpending">Awaiting Moderator Approval</span></p>
    }else{
      const char *zMimetype;
      if( bRawMode ){
        zMimetype = "text/plain";
      }else if( p->pLeaf!=0 ){
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
    @ %z(href("%R/forumpost/%S?t=c",zUuid))[link]</a>
    if( !bRawMode ){
      @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a>
    }
    isPrivate = content_is_private(p->fpid);
    sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0;
    @ </h3>
    forum_emit_post_toggle(zUuid);
    if( isPrivate && !g.perm.ModForum && !sameUser ){
      @ <p><span class="modpending">Awaiting Moderator Approval</span></p>
    }else{
      forum_render(0, bRawMode?"text/plain":pPost->zMimetype, pPost->zWiki,
                   0, 1);
    }
    if( g.perm.WrForum && p->pLeaf==0 ){







<







581
582
583
584
585
586
587

588
589
590
591
592
593
594
    @ %z(href("%R/forumpost/%S?t=c",zUuid))[link]</a>
    if( !bRawMode ){
      @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a>
    }
    isPrivate = content_is_private(p->fpid);
    sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0;
    @ </h3>

    if( isPrivate && !g.perm.ModForum && !sameUser ){
      @ <p><span class="modpending">Awaiting Moderator Approval</span></p>
    }else{
      forum_render(0, bRawMode?"text/plain":pPost->zMimetype, pPost->zWiki,
                   0, 1);
    }
    if( g.perm.WrForum && p->pLeaf==0 ){
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
      while( pIrt && pIrt->fpid!=p->mfirt ) pIrt = pIrt->pPrev;
      if( pIrt ){
        @ in reply to %z(href("%R/forumpost/%S?t=h",pIrt->zUuid))\
        @ %d(pIrt->sid)</a>
      }
    }
    @ </h3>
    forum_emit_post_toggle(zUuid);
    isPrivate = content_is_private(fpid);
    sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0;
    if( isPrivate && !g.perm.ModForum && !sameUser ){
      @ <p><span class="modpending">Awaiting Moderator Approval</span></p>
    }else{
      forum_render(0, pPost->zMimetype, pPost->zWiki, 0, 1);
    }







<







708
709
710
711
712
713
714

715
716
717
718
719
720
721
      while( pIrt && pIrt->fpid!=p->mfirt ) pIrt = pIrt->pPrev;
      if( pIrt ){
        @ in reply to %z(href("%R/forumpost/%S?t=h",pIrt->zUuid))\
        @ %d(pIrt->sid)</a>
      }
    }
    @ </h3>

    isPrivate = content_is_private(fpid);
    sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0;
    if( isPrivate && !g.perm.ModForum && !sameUser ){
      @ <p><span class="modpending">Awaiting Moderator Approval</span></p>
    }else{
      forum_render(0, pPost->zMimetype, pPost->zWiki, 0, 1);
    }
765
766
767
768
769
770
771


















772
773
774
775
776
777
778
    }
    manifest_destroy(pPost);
    @ </div>
  }
  forumthread_delete(pThread);
  return target;
}



















/*
** WEBPAGE: forumpost
**
** Show a single forum posting. The posting is shown in context with
** it's entire thread.  The selected posting is enclosed within
** <div class='forumSel'>...</div>.  Javascript is used to move the







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







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
770
771
772
773
774
775
776
    }
    manifest_destroy(pPost);
    @ </div>
  }
  forumthread_delete(pThread);
  return target;
}

/*
** The first time this is called, it emits SCRIPT tags to load various
** forum-related JavaScript. Ideally it should be called near the end
** of the page, immediately before the call to style_footer() (which
** closes the document's <BODY> and <HTML> tags). Calls after the first
** are a no-op.
*/
static void forum_emit_page_js(){
  static int once = 0;
  if(0==once){
    once = 1;
    style_load_js("forum.js");
    style_emit_script_fossil_bootstrap(0);
    style_emit_script_dom(0);
    style_emit_script_builtin(0, "fossil.page.forumpost.js");
  }
}

/*
** WEBPAGE: forumpost
**
** Show a single forum posting. The posting is shown in context with
** it's entire thread.  The selected posting is enclosed within
** <div class='forumSel'>...</div>.  Javascript is used to move the
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
    style_submenu_element("Complete Thread", "%R/%s/%s?t=a", g.zPath, zName);
    forum_display_history(froot, fpid, 1);
  }else{
    style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName);
    style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName);
    forum_display_hierarchical(froot, fpid);
  }
  style_load_js("forum.js");
  style_footer();
}

/*
** Return true if a forum post should be moderated.
*/
static int forum_need_moderation(void){







|







892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
    style_submenu_element("Complete Thread", "%R/%s/%s?t=a", g.zPath, zName);
    forum_display_history(froot, fpid, 1);
  }else{
    style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName);
    style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName);
    forum_display_hierarchical(froot, fpid);
  }
  forum_emit_page_js();
  style_footer();
}

/*
** Return true if a forum post should be moderated.
*/
static int forum_need_moderation(void){
Changes to src/fossil.dom.js.
70
71
72
73
74
75
76

77
78
79
80
81
82
83
  dom.pre = dom.createElemFactory('pre');
  dom.header = dom.createElemFactory('header');
  dom.footer = dom.createElemFactory('footer');
  dom.section = dom.createElemFactory('section');
  dom.span = dom.createElemFactory('span');
  dom.strong = dom.createElemFactory('strong');
  dom.em = dom.createElemFactory('em');

  dom.img = function(src){
    const e = dom.create('img');
    if(src) e.setAttribute('src',src);
    return e;
  };
  /**
     Creates and returns a new anchor element with the given







>







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  dom.pre = dom.createElemFactory('pre');
  dom.header = dom.createElemFactory('header');
  dom.footer = dom.createElemFactory('footer');
  dom.section = dom.createElemFactory('section');
  dom.span = dom.createElemFactory('span');
  dom.strong = dom.createElemFactory('strong');
  dom.em = dom.createElemFactory('em');
  dom.label = dom.createElemFactory('label');
  dom.img = function(src){
    const e = dom.create('img');
    if(src) e.setAttribute('src',src);
    return e;
  };
  /**
     Creates and returns a new anchor element with the given
Added src/fossil.page.forumpost.js.


















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
(function(F/*the fossil object*/){
  "use strict";
  /* JS code for /forumpage and friends. Requires fossil.dom. */
  const P = fossil.page, D = fossil.dom;
 
  F.onPageLoad(function(){
    const scrollbarIsVisible = (e)=>e.scrollHeight > e.clientHeight;
    const doCollapser = function(forumPostWrapper){
      const content = forumPostWrapper.querySelector('div.forumPostBody');
      if(!scrollbarIsVisible(content)) return;
      const fid = forumPostWrapper.getAttribute('id');
      const cb = D.input('checkbox'), lbl = D.label(),
            cbId = fid+'-expand';
      D.addClass([cb,lbl], 'forum-post-collapser');
      cb.setAttribute('id',cbId);
      lbl.setAttribute('for', cbId)
      forumPostWrapper.insertBefore(cb, content);
      forumPostWrapper.insertBefore(lbl, content);
    };
    document.querySelectorAll(
      'div.forumHier, div.forumTime, div.forumHierRoot'
    ).forEach(doCollapser)
  });
  
})(window.fossil);
Changes to src/main.mk.
224
225
226
227
228
229
230

231
232
233
234
235
236
237
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/fossil.bootstrap.js \
  $(SRCDIR)/fossil.confirmer.js \
  $(SRCDIR)/fossil.dom.js \
  $(SRCDIR)/fossil.fetch.js \
  $(SRCDIR)/fossil.page.fileedit.js \

  $(SRCDIR)/fossil.storage.js \
  $(SRCDIR)/fossil.tabs.js \
  $(SRCDIR)/graph.js \
  $(SRCDIR)/href.js \
  $(SRCDIR)/login.js \
  $(SRCDIR)/markdown.md \
  $(SRCDIR)/menu.js \







>







224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/fossil.bootstrap.js \
  $(SRCDIR)/fossil.confirmer.js \
  $(SRCDIR)/fossil.dom.js \
  $(SRCDIR)/fossil.fetch.js \
  $(SRCDIR)/fossil.page.fileedit.js \
  $(SRCDIR)/fossil.page.forumpost.js \
  $(SRCDIR)/fossil.storage.js \
  $(SRCDIR)/fossil.tabs.js \
  $(SRCDIR)/graph.js \
  $(SRCDIR)/href.js \
  $(SRCDIR)/login.js \
  $(SRCDIR)/markdown.md \
  $(SRCDIR)/menu.js \
Changes to win/Makefile.mingw.
636
637
638
639
640
641
642

643
644
645
646
647
648
649
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/fossil.bootstrap.js \
  $(SRCDIR)/fossil.confirmer.js \
  $(SRCDIR)/fossil.dom.js \
  $(SRCDIR)/fossil.fetch.js \
  $(SRCDIR)/fossil.page.fileedit.js \

  $(SRCDIR)/fossil.storage.js \
  $(SRCDIR)/fossil.tabs.js \
  $(SRCDIR)/graph.js \
  $(SRCDIR)/href.js \
  $(SRCDIR)/login.js \
  $(SRCDIR)/markdown.md \
  $(SRCDIR)/menu.js \







>







636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/fossil.bootstrap.js \
  $(SRCDIR)/fossil.confirmer.js \
  $(SRCDIR)/fossil.dom.js \
  $(SRCDIR)/fossil.fetch.js \
  $(SRCDIR)/fossil.page.fileedit.js \
  $(SRCDIR)/fossil.page.forumpost.js \
  $(SRCDIR)/fossil.storage.js \
  $(SRCDIR)/fossil.tabs.js \
  $(SRCDIR)/graph.js \
  $(SRCDIR)/href.js \
  $(SRCDIR)/login.js \
  $(SRCDIR)/markdown.md \
  $(SRCDIR)/menu.js \
Changes to win/Makefile.msc.
557
558
559
560
561
562
563

564
565
566
567
568
569
570
        "$(SRCDIR)\diff.tcl" \
        "$(SRCDIR)\forum.js" \
        "$(SRCDIR)\fossil.bootstrap.js" \
        "$(SRCDIR)\fossil.confirmer.js" \
        "$(SRCDIR)\fossil.dom.js" \
        "$(SRCDIR)\fossil.fetch.js" \
        "$(SRCDIR)\fossil.page.fileedit.js" \

        "$(SRCDIR)\fossil.storage.js" \
        "$(SRCDIR)\fossil.tabs.js" \
        "$(SRCDIR)\graph.js" \
        "$(SRCDIR)\href.js" \
        "$(SRCDIR)\login.js" \
        "$(SRCDIR)\markdown.md" \
        "$(SRCDIR)\menu.js" \







>







557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
        "$(SRCDIR)\diff.tcl" \
        "$(SRCDIR)\forum.js" \
        "$(SRCDIR)\fossil.bootstrap.js" \
        "$(SRCDIR)\fossil.confirmer.js" \
        "$(SRCDIR)\fossil.dom.js" \
        "$(SRCDIR)\fossil.fetch.js" \
        "$(SRCDIR)\fossil.page.fileedit.js" \
        "$(SRCDIR)\fossil.page.forumpost.js" \
        "$(SRCDIR)\fossil.storage.js" \
        "$(SRCDIR)\fossil.tabs.js" \
        "$(SRCDIR)\graph.js" \
        "$(SRCDIR)\href.js" \
        "$(SRCDIR)\login.js" \
        "$(SRCDIR)\markdown.md" \
        "$(SRCDIR)\menu.js" \
1145
1146
1147
1148
1149
1150
1151

1152
1153
1154
1155
1156
1157
1158
	echo "$(SRCDIR)\diff.tcl" >> $@
	echo "$(SRCDIR)\forum.js" >> $@
	echo "$(SRCDIR)\fossil.bootstrap.js" >> $@
	echo "$(SRCDIR)\fossil.confirmer.js" >> $@
	echo "$(SRCDIR)\fossil.dom.js" >> $@
	echo "$(SRCDIR)\fossil.fetch.js" >> $@
	echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@

	echo "$(SRCDIR)\fossil.storage.js" >> $@
	echo "$(SRCDIR)\fossil.tabs.js" >> $@
	echo "$(SRCDIR)\graph.js" >> $@
	echo "$(SRCDIR)\href.js" >> $@
	echo "$(SRCDIR)\login.js" >> $@
	echo "$(SRCDIR)\markdown.md" >> $@
	echo "$(SRCDIR)\menu.js" >> $@







>







1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
	echo "$(SRCDIR)\diff.tcl" >> $@
	echo "$(SRCDIR)\forum.js" >> $@
	echo "$(SRCDIR)\fossil.bootstrap.js" >> $@
	echo "$(SRCDIR)\fossil.confirmer.js" >> $@
	echo "$(SRCDIR)\fossil.dom.js" >> $@
	echo "$(SRCDIR)\fossil.fetch.js" >> $@
	echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@
	echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@
	echo "$(SRCDIR)\fossil.storage.js" >> $@
	echo "$(SRCDIR)\fossil.tabs.js" >> $@
	echo "$(SRCDIR)\graph.js" >> $@
	echo "$(SRCDIR)\href.js" >> $@
	echo "$(SRCDIR)\login.js" >> $@
	echo "$(SRCDIR)\markdown.md" >> $@
	echo "$(SRCDIR)\menu.js" >> $@