Fossil

Check-in [875472a8]
Login

Check-in [875472a8]

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

Overview
Comment:Fix handling of user-provided classes for unreferenced, joined and overnested footnotes. In all these cases the tokens of user-provided classes are rendered as plain-text and no special classes are added anywhere.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | markdown-footnotes
Files: files | file ages | folders
SHA3-256: 875472a8b0992badd11882ce25e89eb38199d19ab8bde3232d4fe385506b6f88
User & Date: george 2022-02-23 07:36:25
Context
2022-02-23
08:21
Minor refactoring. Move the definition of BLOB_APPEND_BLOB() macro from markdown_html.c to blob.c so that it could be used outside of markdown_html.c. Also rename it to blob_appendb() for consistency with blob_appendf() and other API. Within markdown.c use that newly available macro where appropriate. Within markdown_html.c use it for footnotes-relevant code. Other invocations of BLOB_APPEND_BLOB() within markdown_html.c are left intact (they use an alias) in order to simplify the potential merge with the trunk. ... (check-in: 33a681eb user: george tags: markdown-footnotes)
07:36
Fix handling of user-provided classes for unreferenced, joined and overnested footnotes. In all these cases the tokens of user-provided classes are rendered as plain-text and no special classes are added anywhere. ... (check-in: 875472a8 user: george tags: markdown-footnotes)
2022-02-21
05:14
Add a comment for append_footnote_upc(). Also substitute a variable of zero value with just "0" constant. No functional changes. ... (check-in: ae8a3dd5 user: george tags: markdown-footnotes)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/markdown.c.

140
141
142
143
144
145
146



147
148
149
150
151
152
153
/* link_ref -- reference to a link */
struct link_ref {
  struct Blob id;     /* must be the first field as in footnote struct */
  struct Blob link;
  struct Blob title;
};




struct footnote {
  struct Blob id;      /* must be the first field as in link_ref struct  */
  struct Blob text;    /* footnote's content that is rendered at the end */
  struct Blob upc;     /* user-provided classes  .ASCII-alnum.or-hypen:  */
  int bRndred;         /* indicates if `text` holds a rendered content   */

  int defno;  /* serial number of definition, set during the first pass  */







>
>
>







140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/* link_ref -- reference to a link */
struct link_ref {
  struct Blob id;     /* must be the first field as in footnote struct */
  struct Blob link;
  struct Blob title;
};

/* A footnote's data.
** id, text, and upc fields must be in that particular order.
*/
struct footnote {
  struct Blob id;      /* must be the first field as in link_ref struct  */
  struct Blob text;    /* footnote's content that is rendered at the end */
  struct Blob upc;     /* user-provided classes  .ASCII-alnum.or-hypen:  */
  int bRndred;         /* indicates if `text` holds a rendered content   */

  int defno;  /* serial number of definition, set during the first pass  */
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
  upc_offset = upc_size = 0;
  if( data[i]!='\n' && data[i]!='\r' ){
    size_t j;
    upc_size = is_footnote_classlist(data+i, end-i, 1);
    upc_offset = i; /* prevent further checks for a classlist */
    i += upc_size;
    j = i;
    do i++; while( i<end && data[i]!='\n' && data[i]!='\r' );
    blob_append(&fn.text, data+j, i-j);
    if( i<end ){
      blob_append_char(&fn.text, data[i]);
      i++;
      if( i<end && data[i]=='\n' && data[i-1]=='\r' ){
        blob_append_char(&fn.text, data[i]);
        i++;
      }
    }
  }else{
    i++;
    if( i<end && data[i]=='\n' && data[i-1]=='\r' ) i++;
  }
  if( i<end ){

    /* compute the indentation from the 2nd line  */
    size_t indent = i;
    const char *spaces = data+i;
    while( i<end && data[i]==' ' ){ i++; }
    if( i>=end )   goto footnote_finish;
    indent = i - indent;
    i -= indent;
    if( indent<2 ) goto footnote_finish;

    /* process the 2nd and the following lines */
    while( i+indent<end && memcmp(data+i,spaces,indent)==0 ){
      size_t j;
      i += indent;
      if( !upc_offset ){
        /* a classlist must be provided no later than at the 2nd line */
        upc_offset = i + sizeof_blank_prefix(data+i, end-i, 1);
        upc_size = is_footnote_classlist(data+upc_offset, end-upc_offset, 1);
        if( upc_size ){
          i = upc_offset + upc_size;
        }
      }
      j = i;
      while( i<end && data[i]!='\n' && data[i]!='\r' ) i++;
      blob_append(&fn.text, data+j, i-j);
      if( i>=end ) break;
      blob_append_char(&fn.text, data[i]);
      i++;
      if( i<end && data[i]=='\n' && data[i-1]=='\r' ){
        blob_append_char(&fn.text, data[i]);
        i++;
      }







|
|

















|
|
|
<















|
|







2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534

2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
  upc_offset = upc_size = 0;
  if( data[i]!='\n' && data[i]!='\r' ){
    size_t j;
    upc_size = is_footnote_classlist(data+i, end-i, 1);
    upc_offset = i; /* prevent further checks for a classlist */
    i += upc_size;
    j = i;
    while( i<end && data[i]!='\n' && data[i]!='\r' ){ i++; };
    if( i!=j )blob_append(&fn.text, data+j, i-j);
    if( i<end ){
      blob_append_char(&fn.text, data[i]);
      i++;
      if( i<end && data[i]=='\n' && data[i-1]=='\r' ){
        blob_append_char(&fn.text, data[i]);
        i++;
      }
    }
  }else{
    i++;
    if( i<end && data[i]=='\n' && data[i-1]=='\r' ) i++;
  }
  if( i<end ){

    /* compute the indentation from the 2nd line  */
    size_t indent = i;
    const char *spaces = data+i;
    while( indent<end && data[indent]==' ' ){ indent++; }
    if( indent>=end ) goto footnote_finish;
    indent -= i;

    if( indent<2 ) goto footnote_finish;

    /* process the 2nd and the following lines */
    while( i+indent<end && memcmp(data+i,spaces,indent)==0 ){
      size_t j;
      i += indent;
      if( !upc_offset ){
        /* a classlist must be provided no later than at the 2nd line */
        upc_offset = i + sizeof_blank_prefix(data+i, end-i, 1);
        upc_size = is_footnote_classlist(data+upc_offset, end-upc_offset, 1);
        if( upc_size ){
          i = upc_offset + upc_size;
        }
      }
      j = i;
      while( i<end && data[i]!='\n' && data[i]!='\r' ){ i++; }
      if( i!=j ) blob_append(&fn.text, data+j, i-j);
      if( i>=end ) break;
      blob_append_char(&fn.text, data[i]);
      i++;
      if( i<end && data[i]=='\n' && data[i-1]=='\r' ){
        blob_append_char(&fn.text, data[i]);
        i++;
      }
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693




2694
2695
2696
2697
2698
2699
2700
    int nDups = 0;
    fn = CAST_AS_FOOTNOTES( allNotes );
    qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id);

    /* concatenate footnotes with equal labels */
    for(i=0; i<rndr.notes.nLbled ;){
      struct footnote *x = fn + i;
      size_t j = i+1, k = blob_size(&x->text) + 64;
      while(j<rndr.notes.nLbled && !blob_compare(&x->id, &fn[j].id)){
        k += blob_size(&fn[j].text) + 10;
        j++;
        nDups++;
      }
      if( i+1<j ){
        Blob list = empty_blob;
        blob_reserve(&list, k);
        /* must match _joined_footnote_indicator in html_footnote_item() */
        blob_append_string(&list, "<ul class='fn-joined'>\n");
        for(k=i; k<j; k++){
          struct footnote *y = fn + k;
          blob_append_string(&list, "<li>");




          blob_append(&list, blob_buffer(&y->text), blob_size(&y->text));
          blob_append_string(&list, "</li>\n");

          /* free memory buffer */
          blob_reset(&y->text);
          if( k!=i ) blob_reset(&y->id);
        }







|

|











>
>
>
>







2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
    int nDups = 0;
    fn = CAST_AS_FOOTNOTES( allNotes );
    qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id);

    /* concatenate footnotes with equal labels */
    for(i=0; i<rndr.notes.nLbled ;){
      struct footnote *x = fn + i;
      size_t j = i+1, k = blob_size(&x->text) + 64 + blob_size(&x->upc);
      while(j<rndr.notes.nLbled && !blob_compare(&x->id, &fn[j].id)){
        k += blob_size(&fn[j].text) + 10 + blob_size(&fn[j].upc);
        j++;
        nDups++;
      }
      if( i+1<j ){
        Blob list = empty_blob;
        blob_reserve(&list, k);
        /* must match _joined_footnote_indicator in html_footnote_item() */
        blob_append_string(&list, "<ul class='fn-joined'>\n");
        for(k=i; k<j; k++){
          struct footnote *y = fn + k;
          blob_append_string(&list, "<li>");
          if( blob_size(&y->upc) ){
            blob_append(&list, blob_buffer(&y->upc), blob_size(&y->upc));
            blob_reset(&y->upc);
          }
          blob_append(&list, blob_buffer(&y->text), blob_size(&y->text));
          blob_append_string(&list, "</li>\n");

          /* free memory buffer */
          blob_reset(&y->text);
          if( k!=i ) blob_reset(&y->id);
        }
2769
2770
2771
2772
2773
2774
2775









2776
2777
2778

2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
    }
    release_work_buffer(&rndr,tmp);

    /* footnotes rendering */
    if( rndr.make.footnote_item && rndr.make.footnotes ){
      Blob *all_items = new_work_buffer(&rndr);
      int j = -1;









      for(i=0; i<COUNT_FOOTNOTES(notes); i++){
        const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i;
        if( x->iMark ){

          rndr.make.footnote_item(all_items, &x->text, x->iMark,
                   x->bRndred ? x->nUsed : 0, rndr.make.opaque);
          j = i;
        }
      }
      if( rndr.notes.misref.nUsed ){
        rndr.make.footnote_item(all_items, 0, -1,
                    rndr.notes.misref.nUsed, rndr.make.opaque);
        g.ftntsIssues[0] += rndr.notes.misref.nUsed;
      }
      while( ++j < COUNT_FOOTNOTES(notes) ){
        const struct footnote* x = CAST_AS_FOOTNOTES(notes) + j;
        assert( !x->iMark );
        assert( !x->bRndred );
        assert( (&x->id) + 1 == &x->text ); /* see html_footnote_item() */
        assert( (&x->upc)- 1 == &x->text );
        rndr.make.footnote_item(all_items,&x->text,0,0,rndr.make.opaque);
        g.ftntsIssues[1]++;
      }
      rndr.make.footnotes(ob, all_items, rndr.make.opaque);
      release_work_buffer(&rndr, all_items);
    }
    release_work_buffer(&rndr, notes);







>
>
>
>
>
>
>
>
>


|
>
|
|
|
<









|
<
|







2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797

2798
2799
2800
2801
2802
2803
2804
2805
2806
2807

2808
2809
2810
2811
2812
2813
2814
2815
    }
    release_work_buffer(&rndr,tmp);

    /* footnotes rendering */
    if( rndr.make.footnote_item && rndr.make.footnotes ){
      Blob *all_items = new_work_buffer(&rndr);
      int j = -1;

      /* Assert that the in-memory layout of id, text and upc within
      ** footnote struct matches the expectations of html_footnote_item()
      ** If it doesn't then a compiler has done something very weird.
      */
      const struct footnote *dummy = 0;
      assert( &(dummy->id)  == &(dummy->text) - 1 );
      assert( &(dummy->upc) == &(dummy->text) + 1 );

      for(i=0; i<COUNT_FOOTNOTES(notes); i++){
        const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i;
        if( !x->iMark ) break;
        assert( x->nUsed );
        rndr.make.footnote_item(all_items, &x->text, x->iMark,
                 x->bRndred ? x->nUsed : 0, rndr.make.opaque);
        j = i;

      }
      if( rndr.notes.misref.nUsed ){
        rndr.make.footnote_item(all_items, 0, -1,
                    rndr.notes.misref.nUsed, rndr.make.opaque);
        g.ftntsIssues[0] += rndr.notes.misref.nUsed;
      }
      while( ++j < COUNT_FOOTNOTES(notes) ){
        const struct footnote* x = CAST_AS_FOOTNOTES(notes) + j;
        assert( !x->iMark );
        assert( !x->nUsed );

        assert( !x->bRndred );
        rndr.make.footnote_item(all_items,&x->text,0,0,rndr.make.opaque);
        g.ftntsIssues[1]++;
      }
      rndr.make.footnotes(ob, all_items, rndr.make.opaque);
      release_work_buffer(&rndr, all_items);
    }
    release_work_buffer(&rndr, notes);

Changes to src/markdown_html.c.

445
446
447
448
449
450
451


452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
/* Render a single item of the footnotes list.
 * Each backref gets a unique id to enable dynamic styling. */
static void html_footnote_item(
  struct Blob *ob, const struct Blob *text, int iMark, int nUsed, void *opaque
){
  const struct MarkdownToHtml* ctx = (struct MarkdownToHtml*)opaque;
  const char * const unique = ctx->unique.c;


  assert( nUsed >= 0 );
  /* expect BUGs if the following yields compiler warnings */

  if( iMark < 0 ){                     /* misreferences */
    assert( iMark == -1 );
    if( !nUsed ) return;
    BLOB_APPEND_LITERAL(ob,"<li class='fn-misreference'>"
                              "<sup class='fn-backrefs'>");
    if( nUsed == 1 ){
      blob_appendf(ob,"<a id='misreference%s-a' href='", unique);
      BLOB_APPEND_URI(ob, ctx);
      blob_appendf(ob,"#misref%s-a'>^</a>", unique);
    }else{
      int i;
      blob_append_char(ob, '^');
      for(i=0; i<nUsed && i<26; i++){
        const int c = i + (unsigned)'a';
        blob_appendf(ob," <a id='misreference%s-%c' href='", unique,c);
        BLOB_APPEND_URI(ob, ctx);
        blob_appendf(ob,"#misref%s-%c'>%c</a>", unique,c, c);
      }
      if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
    }
    BLOB_APPEND_LITERAL(ob,"</sup>\n<span>Misreference</span>");

  }else if( iMark > 0 ){               /* a regular footnote */
    char pos[24];
    int bJoin = 0;
    /* make.footnote_item() invocations should pass args accordingly */
    const struct Blob * upc = text+1;
    #define _joined_footnote_indicator "<ul class='fn-joined'>"
    #define _jfi_sz (sizeof(_joined_footnote_indicator)-1)
    assert( text );
    assert( blob_size(text) );
    memset(pos,0,24);
    sprintf(pos, "%s-%i", unique, iMark);








>
>





|



















|


<
<







445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481


482
483
484
485
486
487
488
/* Render a single item of the footnotes list.
 * Each backref gets a unique id to enable dynamic styling. */
static void html_footnote_item(
  struct Blob *ob, const struct Blob *text, int iMark, int nUsed, void *opaque
){
  const struct MarkdownToHtml* ctx = (struct MarkdownToHtml*)opaque;
  const char * const unique = ctx->unique.c;
  /* make.footnote_item() invocations should pass args accordingly */
  const struct Blob *upc = text+1;
  assert( nUsed >= 0 );
  /* expect BUGs if the following yields compiler warnings */

  if( iMark < 0 ){                     /* misreferences */
    assert( iMark == -1 );
    assert( nUsed );
    BLOB_APPEND_LITERAL(ob,"<li class='fn-misreference'>"
                              "<sup class='fn-backrefs'>");
    if( nUsed == 1 ){
      blob_appendf(ob,"<a id='misreference%s-a' href='", unique);
      BLOB_APPEND_URI(ob, ctx);
      blob_appendf(ob,"#misref%s-a'>^</a>", unique);
    }else{
      int i;
      blob_append_char(ob, '^');
      for(i=0; i<nUsed && i<26; i++){
        const int c = i + (unsigned)'a';
        blob_appendf(ob," <a id='misreference%s-%c' href='", unique,c);
        BLOB_APPEND_URI(ob, ctx);
        blob_appendf(ob,"#misref%s-%c'>%c</a>", unique,c, c);
      }
      if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
    }
    BLOB_APPEND_LITERAL(ob,"</sup>\n<span>Misreference</span>");

  }else if( iMark > 0 ){  /* regular, joined and overnested footnotes */
    char pos[24];
    int bJoin = 0;


    #define _joined_footnote_indicator "<ul class='fn-joined'>"
    #define _jfi_sz (sizeof(_joined_footnote_indicator)-1)
    assert( text );
    assert( blob_size(text) );
    memset(pos,0,24);
    sprintf(pos, "%s-%i", unique, iMark);

519
520
521
522
523
524
525
526
527
528
529
530

531
532
533
534



535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551



552
553
554
555
556
557
558
        blob_appendf(ob," <a id='footnote%s-%s' href='", pos, l.c);
        BLOB_APPEND_URI(ob, ctx);
        blob_appendf(ob,"#noteref%s-%s'>%s</a>", pos,l.c, l.c);
      }
      if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
    }
    BLOB_APPEND_LITERAL(ob,"</sup>\n");
    append_footnote_upc(ob, upc, 1);
    if( bJoin ){
      BLOB_APPEND_LITERAL(ob,"<sup class='fn-joined'></sup><ul>");
      blob_append(ob,blob_buffer(text)+_jfi_sz,blob_size(text)-_jfi_sz);
    }else if( nUsed ){

      BLOB_APPEND_BLOB(ob, text);
    }else{
      BLOB_APPEND_LITERAL(ob,"<i></i>\n"
          "<pre><code class='language-markdown'>");



      html_escape(ob, blob_buffer(text), blob_size(text));
      BLOB_APPEND_LITERAL(ob,"</code></pre>");
    }
    #undef _joined_footnote_indicator
    #undef _jfi_sz
  }else{
    /* a footnote was defined but wasn't used */
    /* make.footnote_item() invocations should pass args accordingly */
    const struct Blob * id = text-1;
    assert( !nUsed );
    assert( text );
    assert( blob_size(text) );
    assert( blob_size(id) );
    BLOB_APPEND_LITERAL(ob,"<li class='fn-unreferenced'>\n[^&nbsp;<code>");
    html_escape(ob, blob_buffer(id), blob_size(id));
    BLOB_APPEND_LITERAL(ob, "</code>&nbsp;]<i></i>\n"
        "<pre><code class='language-markdown'>");



    html_escape(ob, blob_buffer(text), blob_size(text));
    BLOB_APPEND_LITERAL(ob,"</code></pre>");
  }
  BLOB_APPEND_LITERAL(ob, "\n</li>\n");
}
static void html_footnotes(
  struct Blob *ob, const struct Blob *items, void *opaque







<




>




>
>
>





<
|










>
>
>







519
520
521
522
523
524
525

526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542

543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
        blob_appendf(ob," <a id='footnote%s-%s' href='", pos, l.c);
        BLOB_APPEND_URI(ob, ctx);
        blob_appendf(ob,"#noteref%s-%s'>%s</a>", pos,l.c, l.c);
      }
      if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
    }
    BLOB_APPEND_LITERAL(ob,"</sup>\n");

    if( bJoin ){
      BLOB_APPEND_LITERAL(ob,"<sup class='fn-joined'></sup><ul>");
      blob_append(ob,blob_buffer(text)+_jfi_sz,blob_size(text)-_jfi_sz);
    }else if( nUsed ){
      append_footnote_upc(ob, upc, 1);
      BLOB_APPEND_BLOB(ob, text);
    }else{
      BLOB_APPEND_LITERAL(ob,"<i></i>\n"
          "<pre><code class='language-markdown'>");
      if( blob_size(upc) ){
        BLOB_APPEND_BLOB(ob, upc);
      }
      html_escape(ob, blob_buffer(text), blob_size(text));
      BLOB_APPEND_LITERAL(ob,"</code></pre>");
    }
    #undef _joined_footnote_indicator
    #undef _jfi_sz

  }else{             /* a footnote was defined but wasn't referenced */
    /* make.footnote_item() invocations should pass args accordingly */
    const struct Blob * id = text-1;
    assert( !nUsed );
    assert( text );
    assert( blob_size(text) );
    assert( blob_size(id) );
    BLOB_APPEND_LITERAL(ob,"<li class='fn-unreferenced'>\n[^&nbsp;<code>");
    html_escape(ob, blob_buffer(id), blob_size(id));
    BLOB_APPEND_LITERAL(ob, "</code>&nbsp;]<i></i>\n"
        "<pre><code class='language-markdown'>");
    if( blob_size(upc) ){
      BLOB_APPEND_BLOB(ob, upc);
    }
    html_escape(ob, blob_buffer(text), blob_size(text));
    BLOB_APPEND_LITERAL(ob,"</code></pre>");
  }
  BLOB_APPEND_LITERAL(ob, "\n</li>\n");
}
static void html_footnotes(
  struct Blob *ob, const struct Blob *items, void *opaque

Changes to test/markdown-test3.md.

99
100
101
102
103
104
105







106
107
108
109
110
111
112
  span.notescope.fn-upc-example sup.noteref::after {
    content: " ⛄";
  }
  sup.noteref.fn-upc-example:hover::after,
  span.notescope.fn-upc-example sup.noteref:hover::after {
    content: " 👻";
  }







</style>

It is possible to provide a list of classes for a particular footnote and
all its references. This is achieved by prepending a footnote's text with
a special token that starts with dot and ends with colon.
(^
   .alpha-Numeric123.EXAMPLE:







>
>
>
>
>
>
>







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  span.notescope.fn-upc-example sup.noteref::after {
    content: " ⛄";
  }
  sup.noteref.fn-upc-example:hover::after,
  span.notescope.fn-upc-example sup.noteref:hover::after {
    content: " 👻";
  }
  li.fn-upc-l span.fn-upc  {
    font-size: 60%;
    color: orange;
  }
  li.fn-upc-l span.fn-upc span.fn-upcDot {
    display: none;
  }
</style>

It is possible to provide a list of classes for a particular footnote and
all its references. This is achieved by prepending a footnote's text with
a special token that starts with dot and ends with colon.
(^
   .alpha-Numeric123.EXAMPLE:
122
123
124
125
126
127
128




129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
If a footnote consists just of a valid userclass token then this token
is not interpreted as such, instead it is emitted as plain text.
(^  
   .bare.classlist.inside.inline.footnote:  
)[^bare1]
[^bare2]





<html>
  Click
  <a href="?a=B&quote='&nonASCII=😂&script=<script>alert('Broken!');</script>">
  here</a> and
  <a href='?a=B&quote="&nonASCII=😂&script=<script>alert("Broken!");</script>'>
  here</a>
  to test escaping of REQUEST_URI in the generated footnote markers.
</html>

A depth of nesting must be limited.
(^
 A long chain of nested inline footnotes...
 (^
  is a rather unusual thing...
  (^
   and requires extra CPU cycles for processing.
   (^
    Theoretically speaking O(n<sup>2</sup>).
    (^
     Thus it is worth dismissing those footnotes...
     (^
      that are nested deeper than on a certain level.
      (^
       A particular value for that limit...
       (^
        is hard-coded in src/markdown.c ...
        (^
         in function `markdown()` ...
         (^
          in variable named `maxDepth`.
          (^







>
>
>
>











|

|

|

|

|

|

|







129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
If a footnote consists just of a valid userclass token then this token
is not interpreted as such, instead it is emitted as plain text.
(^  
   .bare.classlist.inside.inline.footnote:  
)[^bare1]
[^bare2]

[^duplicate]: .with.UPC.token:   
   When duplicates are joined their UPC tokens are treated as plain-text.
   Blank characters between token and main text must be preserved.

<html>
  Click
  <a href="?a=B&quote='&nonASCII=😂&script=<script>alert('Broken!');</script>">
  here</a> and
  <a href='?a=B&quote="&nonASCII=😂&script=<script>alert("Broken!");</script>'>
  here</a>
  to test escaping of REQUEST_URI in the generated footnote markers.
</html>

A depth of nesting must be limited.
(^
 .L.1: A long chain of nested inline footnotes...
 (^
  .L.2: is a rather unusual thing...
  (^
   .L.3: and requires extra CPU cycles for processing.
   (^
    .L.4: Theoretically speaking O(n<sup>2</sup>).
    (^
     .L.5: Thus it is worth dismissing those footnotes...
     (^
      .L.6: that are nested deeper than on a certain level.
      (^
       .L.7: A particular value for that limit...
       (^
        is hard-coded in src/markdown.c ...
        (^
         in function `markdown()` ...
         (^
          in variable named `maxDepth`.
          (^
214
215
216
217
218
219
220



221
222
223
224
225
226
227
228
229
230
231
232

[^bare1]:  .at.the.1st.line.of.labeled.footnote.definition:
     

[^bare2]:  
           .at.the.2nd.line.of.labeled.footnote.definition:
           




[^nostyle]:
  .unused.classes:
  In that case text of the footnote just looks like as if
  no special processing occured.


[^ <script>alert("You have been pwned!");</script> ]: Labels are escaped

[^ <textarea>"Last words here...' ]:
  <textarea>Content is also escaped</textarea>








>
>
>












225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

[^bare1]:  .at.the.1st.line.of.labeled.footnote.definition:
     

[^bare2]:  
           .at.the.2nd.line.of.labeled.footnote.definition:
           
[^stray with UPC]: .UPC-token:
    A token of user-provided classes must be rendered within strays.
    Aslo: this and the previous line may not have extra indentation.

[^nostyle]:
  .unused.classes:
  In that case text of the footnote just looks like as if
  no special processing occured.


[^ <script>alert("You have been pwned!");</script> ]: Labels are escaped

[^ <textarea>"Last words here...' ]:
  <textarea>Content is also escaped</textarea>