Fossil

Check-in Differences
Login

Check-in Differences

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

Difference From d88444e265f3f78f To 67060c509079a8b7

2020-09-17
23:44
pikchr command doc updates. ... (check-in: 82a0b517 user: stephan tags: trunk)
23:25
Removed some console.debug() output and fixed extraneous breaks which caused markdown_to_html() to stop afer processing a single tag. ... (check-in: 67060c50 user: stephan tags: trunk)
23:11
Renovated the pikchr click handling as discussed off-list with drh. ... (check-in: 938bb6c7 user: stephan tags: trunk)
21:09
Toggle Pikchr between SVG and source code using ctrl-click. Or if the Pikchr was created using the "toggle" tag, an plain old single-click will suffice. ... (check-in: d88444e2 user: drh tags: trunk)
19:31
Misuse of a potentially unsigned value, caught by clang. Only affected platforms where char is unsigned by default. ... (check-in: 5c92bbfc user: stephan tags: trunk)

Changes to src/default.css.

1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
noscript > .error {
  /* Part of the style_emit_noscript_for_js_page() interface. */
  padding: 1em;
  font-size: 150%;
}
.pikchr-src { /* source code view for a pikchr (see fossil.pikchr.js) */
  box-sizing: border-box/*reduces UI shift*/;
  border-width: 1px;
  border-style: dotted;
  overflow: auto;
}







<
<


1386
1387
1388
1389
1390
1391
1392


1393
1394
noscript > .error {
  /* Part of the style_emit_noscript_for_js_page() interface. */
  padding: 1em;
  font-size: 150%;
}
.pikchr-src { /* source code view for a pikchr (see fossil.pikchr.js) */
  box-sizing: border-box/*reduces UI shift*/;


  overflow: auto;
}

Changes to src/fossil.dom.js.

711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
  };

  /**
     Parses a string as HTML.

     Usages:

     (htmlString)
     (DOMElement target, htmlString)

     The first form parses the string as HTML and returns an Array of
     all elements parsed from it. If string is falsy then it returns
     an empty array.

     The second form parses the HTML string and appends all elements
     to the given target element using dom.append(), then returns the







|
|







711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
  };

  /**
     Parses a string as HTML.

     Usages:

     Array (htmlString)
     DOMElement (DOMElement target, htmlString)

     The first form parses the string as HTML and returns an Array of
     all elements parsed from it. If string is falsy then it returns
     an empty array.

     The second form parses the HTML string and appends all elements
     to the given target element using dom.append(), then returns the

Changes to src/fossil.page.pikchrshow.js.

320
321
322
323
324
325
326
327
328
329

330
331
332
333
334
335
336
    D.enable(this.e.previewModeToggle, this.e.markupAlignRadios);
    let label, svg;
    switch(this.previewMode){
    case 0:
      label = "SVG";
      f.showMarkupAlignment(false);
      D.parseHtml(D.clearElement(preTgt), P.response.raw);
      svg = f.getSvgNode(this.response.raw);
      if(svg){ /*for copy button*/
        this.e.taPreviewText.value = svg.outerHTML;

      }
      break;
    case 1:
      label = "Markdown";
      f.showMarkupAlignment(true);
      this.e.taPreviewText.value = [
        '```pikchr'+f.getMarkupAlignmentClass(),







|


>







320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
    D.enable(this.e.previewModeToggle, this.e.markupAlignRadios);
    let label, svg;
    switch(this.previewMode){
    case 0:
      label = "SVG";
      f.showMarkupAlignment(false);
      D.parseHtml(D.clearElement(preTgt), P.response.raw);
      svg = preTgt.querySelector('svg.pikchr');
      if(svg){ /*for copy button*/
        this.e.taPreviewText.value = svg.outerHTML;
        F.pikchr.addSrcView(svg);
      }
      break;
    case 1:
      label = "Markdown";
      f.showMarkupAlignment(true);
      this.e.taPreviewText.value = [
        '```pikchr'+f.getMarkupAlignmentClass(),

Changes to src/fossil.pikchr.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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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
106
107
108
109
110
111

112
113
114
115
116
117
118
119
120
121
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
(function(F/*window.fossil object*/){
  "use strict";

  const D = F.dom;

  const P = F.pikchr = {
  };

  ////////////////////////////////////////////////////////////////////////
  // Install an app-specific stylesheet, just for development, after which
  // it will be moved into default.css
  (function(){
    const head = document.head || document.querySelector('head'),
          styleTag = document.createElement('style'),
          wh = '1cm' /* fixed width/height of buttons */,
          styleCSS = `
.pikchr-button-bar {
  position: absolute;
  position: absolute;
  top: 0;
  left: 0;
  display: inline-flex;
  flex-direction: column;
}
.pikchr-src-button {
  min-height: ${wh}; max-height: ${wh};
  min-width: ${wh}; max-width: ${wh};
  font-size: ${wh};
  border: 1px solid black;
  background-color: rgba(255,255,0,0.7);
  border-radius: 0.25cm;
  z-index: 50;
  cursor: pointer;
  text-align: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transform-origin: center;
  transition: transform 250ms linear;
  padding: 0; margin: 0;
/* MIT-licensed SVG from: https://github.com/leungwensen/svg-icon/blob/master/dist/svg/ant/code.svg */
  background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg viewBox='0 0 1195 1195' \
xmlns='http:/\x2fwww.w3.org/2000/svg'%3e%3cpath d='M321.333 440q-9-10-22.5-10t-22.5 \
10l-182 181q-9 9-9 22.5t9 22.5l182 182q9 10 22.5 10t22.5-10q10-9 10-22.5t-10-22.5l-159-159 \
159-159q10-10 10-23t-10-22zm552 0q9-10 22.5-10t22.5 10l182 181q9 9 9 22.5t-9 \
22.5l-182 182q-9 10-22.5 10t-22.5-10q-10-9-10-22.5t10-22.5l159-159-159-159q-10-10-10-23t10-22zm-97-180q12 \
6 16 19t-2 24l-371 704q-7 12-19.5 16t-24.5-2q-11-7-15-19.5t2-24.5l371-703q6-12 \
18.5-16t24.5 2z'/%3e%3c/svg%3e");
  background-size: contain;
}
.pikchr-src-button.src-active {
  transform: scaleX(-1);
/* MIT-licensed SVG from: https://github.com/leungwensen/svg-icon/blob/master/dist/svg/ant/picture.svg */
  background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg viewBox='0 0 1195 1195' \
xmlns='http:/\x2fwww.w3.org/2000/svg'%3e%3cpath d='M1045.333 192h-896q-26 0-45 \
19t-19 45v768q0 26 19 45t45 19h896q26 0 45-19t19-45V256q0-26-19-45t-45-19zm-896 \
64h896v714l-236-348q-7-12-21-14t-25 7l-154 125-174-243q-10-14-28-13t-26 17l-232 \
448V256zm855 768h-822l231-447 184 255 179-145zm-182-642q13 0 22.5 9.5t9.5 22.5-9.5 \
22.5-22.5 9.5-22.5-9.5-9.5-22.5 9.5-22.5 22.5-9.5zm0-64q-40 0-68 28t-28 68 28 68 68 \
28 68-28 28-68-28-68-68-28z'/%3e%3c/svg%3e");
}
textarea.pikchr-src-text {
  box-sizing: border-box/*reduces UI shift*/;
}
.pikchr-copy-button {
  min-width: ${wh}; max-width: ${wh};
  min-height: ${wh}; max-height: ${wh};
  display: inline-block;
}
.pikchr-button-bar > .pikchr-src-button,
.pikchr-button-bar > .pikchr-copy-button {
  margin-bottom: 0.3em;
}
`;
    head.appendChild(styleTag);
    /* Adapted from https://stackoverflow.com/a/524721 */
    styleTag.type = 'text/css';
    D.append(styleTag, styleCSS);
  })();

  /**
     Sets up a "view source" button on one or more pikchr-created SVG
     image elements.


     The first argument may be any of:

     - A single SVG element.

     - A collection (with a forEach method) of such elements.

     - A CSS selector string for one or more such elements.

     - An array of such strings.

     Passing no value is equivalent to passing 'svg.pikchr'.

     For each SVG in the resulting set, this function does the
     following:

     - It sets the "position" value of the element's *parent* node to
     "relative", as that is necessary for what follows.

     - It creates a small pseudo-button, adding it to the SVG
     element's parent node, styled to hover in one of the element's
     corners.

     - That button, when tapped, toggles the SVG on and off
     while revealing or hiding a readonly textarea element
     which contains the source code for that pikchr SVG
     (which pikchr has helpfully embedded in the SVG's
     metadata).


     Returns this object.

     The 2nd argument is intended to be a plain options object, but it
     is currently unused, as it's not yet clear what we can/should
     make configurable.

     Each element will only be processed once by this routine, even if
     it is passed to this function multiple times. Each processed
     element gets a "data" attribute set to it to indicate that it was
     already dealt with.





  */
  P.addSrcView = function f(svg,opt){
    if(!f.hasOwnProperty('bodyClick')){
      f.bodyClick = function(){






        D.addClass(document.querySelectorAll('.pikchr-button-bar'), 'hidden');




      };
      document.body.addEventListener('click', f.bodyClick, false);
    }
    if(!svg) svg = 'svg.pikchr';
    if('string' === typeof svg){
      document.querySelectorAll(svg).forEach(
        (e)=>f.call(this, e, opt)
      );
      return this;
    }else if(svg.forEach){
      svg.forEach((e)=>f.call(this, e, opt));
      return this;
    }
    if(svg.dataset.pikchrProcessed){
      return this;
    }
    svg.dataset.pikchrProcessed = 1;
    const parent = svg.parentNode;
    parent.style.position = 'relative' /* REQUIRED for btn placement */;
    const srcView = parent.querySelector('.pikchr-src');
    if(!srcView){
      console.warn("No pikchr source node found in",parent);

      return this;
    }
    const buttonBar = D.addClass(D.span(), 'pikchr-button-bar');
    const btnFlip = D.append(
      D.addClass(D.span(), 'pikchr-src-button'),
    );
    const btnCopy = F.copyButton(D.span(), {
      cssClass: ['copy-button', 'pikchr-copy-button'],
      extractText: ()=>(srcView.classList.contains('hidden') ? svg.outerHTML : srcView.value),
      oncopy: ()=>D.flashOnce(btnCopy, ()=>btnFlip.click())
      // ^^^ after copying and flashing, flip back to SVG mode. */
    });
    D.append(buttonBar, btnFlip, btnCopy);
    // not yet sure which options we can/should support:
    // opt = F.mergeLastWins({},opt);
    D.addClass(srcView, 'hidden')/*should already be so, but just in case*/;
    D.append(parent, D.addClass(buttonBar, 'hidden'));

    parent.addEventListener('click', function f(ev){
      ev.preventDefault();
      ev.stopPropagation();
      D.toggleClass(buttonBar, 'hidden');
    }, false);

    /** Show the mode-switch buttons only in source view, and switch to
        source view if the SVG is tapped. This allows easy switching to
        source view while also keeping the buttons out of the way in
        SVG mode and giving the user the option of select/copy in the
        source mode via normal text-selection approaches. */
    svg.addEventListener('click', function(ev){
      D.removeClass(buttonBar, 'hidden');
      btnFlip.click();
    }, false);

    /** Toggle the source/SVG view on click. */
    btnFlip.addEventListener('click', function f(ev){
      ev.preventDefault();
      ev.stopPropagation();
      if(!f.hasOwnProperty('origMaxWidth')){
        f.origMaxWidth = parent.style.maxWidth;
      }
      const svgStyle = window.getComputedStyle(svg);
      srcView.style.minWidth = svgStyle.width;
      srcView.style.minHeight = svgStyle.height;
      /* ^^^ The SVG wrapper/parent element has a max-width, so the
         textarea will be too small on tiny images and won't be
         enlargable. */
      if(0){
        /* We seem to have a fundamental incompatibility with how we
           really want to position srcView at the same pos/size as the
           svg and how that interacts with centered items.
           Until/unless this can be solved, we have to decide between
           the lesser of two evils:

           1) This option. Small images have uselessly tiny source
           view which cannot be enlarged because the parent element
           has a width and/or max-width. width/max-width are important
           for center alignment via the margin:auto trick.

           2) Center-aligned images shift all the way to the left when
           the source view is visible, then back to the center when
           source view is hidden. Source views are resizable and may
           even grow a bit automatically for tiny images.
        */
        if(srcView.classList.contains('hidden')){/*initial state*/
          parent.style.width = f.origMaxWidth;
          parent.style.maxWidth = 'unset';
        }else{/*srcView is active*/
          parent.style.maxWidth = f.origMaxWidth;
          parent.style.width = 'unset';
        }
      }else if(1){
        /* Option #2: gives us good results for non-centered items but
           not for centered. We apparently have no(?) reliable way of
           distinguishing centered from left/indented pikchrs here
           unless we add a CSS class to mark them as such in the
           pikchr-to-wiki-image code. */
        if(srcView.classList.contains('hidden')){/*initial state*/
          parent.style.width = 'unset';
          parent.style.maxWidth = 'unset';
        }else{/*srcView is active*/
          parent.style.maxWidth = f.origMaxWidth;
          parent.style.width = 'unset';
        }
      }
      btnFlip.classList.toggle('src-active');
      D.toggleClass([svg, srcView, buttonBar], 'hidden');
    }, false);
  };
  
})(window.fossil);


<
|

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

<
|
>



|









|
|
|
|
<
|
<
<
<

|
<
|
<
<
>



<
<
<
<




>
>
>
>
>

|
|
|
>
>
>
>
>
>
|
>
>
>
>

<
|


|
<
<


|







<
|
|
<
>


|
<
<
<
<
<
|
<
<
<
<
<
<
|
<
|
|
<
<
<
<
|
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

<

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


29
30
31
32




33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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
(function(F/*window.fossil object*/){
  "use strict";

  const D = F.dom, P = F.pikchr = {};












































































  /**

     Initializes pikchr-rendered elements with the ability to
     toggle between their SVG and source code.

     The first argument may be any of:

     - A single SVG.pikchr element.

     - A collection (with a forEach method) of such elements.

     - A CSS selector string for one or more such elements.

     - An array of such strings.

     Passing no value is equivalent to passing 'svg.pikchr'.

     For each SVG in the resulting set, this function sets up event
     handlers which allow the user to toggle the SVG between image and
     source code modes. The image will switch modes in response to
     cltr-click and, if its *parent* element has the "toggle" CSS

     class, it will also switch modes in response to single-click.




     If the parent element has the "source" CSS class, the image

     starts off with its source code visible and the image hidden,


     instead of the default of the other way around.

     Returns this object.





     Each element will only be processed once by this routine, even if
     it is passed to this function multiple times. Each processed
     element gets a "data" attribute set to it to indicate that it was
     already dealt with.

     This code expects the following structure around the SVGs, and
     will not process any which don't match this:

     <DIV><SVG.pikchr></SVG><PRE.pikchr-src></PRE></DIV>
  */
  P.addSrcView = function f(svg){
    if(!f.hasOwnProperty('parentClick')){
      f.parentClick = function(ev){
        if(ev.ctrlKey || this.classList.contains('toggle')){
          this._childs.forEach((e)=>e.classList.toggle('hidden'));
        }
        /* For the sake of small pics, we have to eliminate the
           parent element's max-width... */
        const src = this._childs[1];
        if(src.classList.contains('hidden')){
          this.style.maxWidth = this.dataset.origMaxWidth;
        }else{
          this.style.maxWidth = "unset";
        }
      };

    };
    if(!svg) svg = 'svg.pikchr';
    if('string' === typeof svg){
      document.querySelectorAll(svg).forEach((e)=>f.call(this, e));


      return this;
    }else if(svg.forEach){
      svg.forEach((e)=>f.call(this, e));
      return this;
    }
    if(svg.dataset.pikchrProcessed){
      return this;
    }
    svg.dataset.pikchrProcessed = 1;
    const parent = svg.parentNode;

    const srcView = svg.nextElementSibling;
    if(!srcView || !srcView.classList.contains('pikchr-src')){

      /* Without this element, there's nothing for us to do here. */
      return this;
    }
    parent.dataset.origMaxWidth = parent.style.maxWidth;





    parent._childs = [svg, srcView];






    D.addClass(srcView, 'hidden');

    D.removeClass(svg, 'hidden');
    parent.addEventListener('click', f.parentClick, false);




    if(parent.classList.contains('source')){









      /* Start off in source-view mode via a very fake click event */



















      f.parentClick.call(parent, {ctrlKey:true});




    }





























  };

})(window.fossil);

Changes to src/markdown_html.c.

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
388
389
390
391

392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
** text and insert the result in place of the original.
*/
void pikchr_to_html(
  Blob *ob,                     /* Write the generated SVG here */
  const char *zSrc, int nSrc,   /* The Pikchr source text */
  const char *zArg, int nArg    /* Addition arguments */
){
  int w = 0, h = 0;
  char *zIn = fossil_strndup(zSrc, nSrc);
  char *zOut = pikchr(zIn, "pikchr", 0, &w, &h);
  if( w>0 && h>0 ){
    static int nSvg = 0;
    const char *zSafeNonce = safe_html_nonce(1);
    const char *zCss = "";
    const char *zClickOk = "e.ctrlKey";
    blob_append(ob, zSafeNonce, -1);
    blob_append_char(ob, '\n');
    while( nArg>0 ){
      int i;
      for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){}
      if( i==6 && strncmp(zArg, "center", 6)==0 ){
        zCss = "display:block;margin:auto;";

      }else if( i==6 && strncmp(zArg, "indent", 6)==0 ){
        zCss = "margin-left:4em;";

      }else if( i==10 && strncmp(zArg, "float-left", 10)==0 ){
        zCss = "float:left;padding=4em;";

      }else if( i==11 && strncmp(zArg, "float-right", 11)==0 ){
        zCss = "float:right;padding=4em;";

      }else if( i==6 && strncmp(zArg, "toggle", 6)==0 ){
        zClickOk = "1";



      }
      while( i<nArg && fossil_isspace(zArg[i]) ){ i++; }
      zArg += i;
      nArg -= i;
    }
    blob_appendf(ob, "<div id='svgid-%d'>\n", ++nSvg);
    blob_appendf(ob, "<div class='pikchr-svg'");
    blob_appendf(ob, " style='max-width:%dpx;%s'>\n", w, zCss);
    blob_append(ob, zOut, -1);
    blob_appendf(ob, "</div>\n");
    blob_appendf(ob, "<pre class='hidden'><code>"
                     "%s</code></pre>\n", zIn);
    blob_appendf(ob, "</div>\n");
    blob_appendf(ob,
      "<script nonce='%s'>\n"
      "document.getElementById('svgid-%d').onclick=function(e){\n"
      "  if(%s){\n"
      "    for(var c of this.children){c.classList.toggle('hidden');}\n"
      "  }\n"
      "}\n"
      "</script>\n",
      style_nonce(), nSvg, zClickOk);

    blob_appendf(ob, "%s\n", zSafeNonce);
  }else{
    blob_appendf(ob, "<pre>\n%s\n</pre>\n", zOut);
  }
  fossil_free(zIn);
  free(zOut);
}



/* Invoked for `...` blocks where there are nSep grave accents in a
** row that serve as the delimiter.  According to CommonMark:
**
**   *  https://spec.commonmark.org/0.29/#fenced-code-blocks
**   *  https://spec.commonmark.org/0.29/#code-spans
**







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







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
** text and insert the result in place of the original.
*/
void pikchr_to_html(
  Blob *ob,                     /* Write the generated SVG here */
  const char *zSrc, int nSrc,   /* The Pikchr source text */
  const char *zArg, int nArg    /* Addition arguments */
){
  int pikFlags = PIKCHR_PROCESS_NONCE
    | PIKCHR_PROCESS_DIV
    | PIKCHR_PROCESS_SRC_HIDDEN;





  Blob bSrc = empty_blob;

  while( nArg>0 ){
    int i;
    for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){}
    if( i==6 && strncmp(zArg, "center", 6)==0 ){

      pikFlags |= PIKCHR_PROCESS_DIV_CENTER;
    }else if( i==6 && strncmp(zArg, "indent", 6)==0 ){

      pikFlags |= PIKCHR_PROCESS_DIV_INDENT;
    }else if( i==10 && strncmp(zArg, "float-left", 10)==0 ){

      pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_LEFT;
    }else if( i==11 && strncmp(zArg, "float-right", 11)==0 ){

      pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_RIGHT;
    }else if( i==6 && strncmp(zArg, "toggle", 6)==0 ){

      pikFlags |= PIKCHR_PROCESS_DIV_TOGGLE;
    }else if( i==6 && strncmp(zArg, "source", 6)==0 ){
      pikFlags |= PIKCHR_PROCESS_DIV_SOURCE;
    }
    while( i<nArg && fossil_isspace(zArg[i]) ){ i++; }
    zArg += i;
    nArg -= i;
  }








  blob_append(&bSrc, zSrc, nSrc)








    /*have to dup input to ensure a NUL-terminated source string */;
  pikchr_process(blob_str(&bSrc), pikFlags, 0, ob);






  blob_reset(&bSrc);
}

/* Invoked for `...` blocks where there are nSep grave accents in a
** row that serve as the delimiter.  According to CommonMark:
**
**   *  https://spec.commonmark.org/0.29/#fenced-code-blocks
**   *  https://spec.commonmark.org/0.29/#code-spans
**

Changes to src/pikchrshow.c.

31
32
33
34
35
36
37


38
39
40
41

42
43
44
45
46
47
48
49
50
51
52
53
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


106
107
108
109
110
111
112
#define PIKCHR_PROCESS_SRC        0x10
#define PIKCHR_PROCESS_SRC_HIDDEN 0x20
#define PIKCHR_PROCESS_DIV        0x40
#define PIKCHR_PROCESS_DIV_INDENT      0x0100
#define PIKCHR_PROCESS_DIV_CENTER      0x0200
#define PIKCHR_PROCESS_DIV_FLOAT_LEFT  0x0400
#define PIKCHR_PROCESS_DIV_FLOAT_RIGHT 0x0800


#endif

/*
** Processes a pikchr script, optionally with embedded TH1. zIn is the

** input script. pikFlags may be a bitmask of any of the
** PIKCHR_PROCESS_xxx flags (see below). thFlags may be a bitmask of
** any of the TH_INIT_xxx and/or TH_R2B_xxx flags. Output is sent to
** pOut, appending to it without modifying any prior contents.
**
** Returns 0 on success, 1 if TH1 processing failed, or 2 if pikchr
** processing failed. In either case, the error message (if any) from
** TH1 or pikchr will be appended to pOut.
**
** pikFlags flag descriptions:
**
** - PIKCHR_PROCESS_TH1 means to run zIn through TH1, using the TH1
** init flags specified in the 3rd argument. If thFlags is non-0 then
** this flag is assumed even if it is not specified.
**
** - PIKCHR_PROCESS_TH1_NOSVG means that processing stops after the
** TH1 step, thus the output will be (presumably) a
** TH1-generated/processed pikchr script (or whatever else the TH1
** outputs). If this flag is set, PIKCHR_PROCESS_TH1 is assumed even
** if it is not specified.
**
** The remaining flags listed below are ignored if
** PIKCHR_PROCESS_TH1_NOSVG is specified:
**
** - PIKCHR_PROCESS_DIV: if set, the SVG result is wrapped in a DIV
** element which specifies a max-width style value based on the SVG's
** calculated size. This flag has multiple mutually exclusive forms:
**
**  - PIKCHR_PROCESS_DIV uses default element alignment.
**  - PIKCHR_PROCESS_DIV_INDENT indents the div.
**  - PIKCHR_PROCESS_DIV_CENTER centers the div.
**  - PIKCHR_PROCESS_DIV_FLOAT_LEFT floats the div left.
**  - PIKCHR_PROCESS_DIV_FLOAT_RIGHT floats the div right.
**
** If more than one is specified, which one is used is undefined.












**
** - PIKCHR_PROCESS_NONCE: if set, the resulting SVG/DIV are wrapped
** in "safe nonce" comments, which are a fossil-internal mechanism
** which prevents the wiki/markdown processors from re-processing this
** output.


**
** - PIKCHR_PROCESS_SRC: if set, a new TEXTAREA.pikchr-src element is injected
** adjacet to the SVG element which contains the HTML-escaped content of
** the input script.
**
** - PIKCHR_PROCESS_SRC_HIDDEN: exactly like PIKCHR_PROCESS_SRC but
** the .pikchr-src tag also gets the CSS class 'hidden' (which, in
** fossil's default CSS, will hide that element).


**
** - PIKCHR_PROCESS_ERR_PRE: if set and pikchr() fails, the resulting
** error report is wrapped in a PRE element, else it is retained
** as-is (intended for console output).
*/
int pikchr_process(const char * zIn, int pikFlags, int thFlags,
                    Blob * pOut){
  Blob bIn = empty_blob;
  int isErr = 0;

  if(!(PIKCHR_PROCESS_DIV & pikFlags)
     /* If any DIV_xxx flags are set, set DIV */
     && (PIKCHR_PROCESS_DIV_INDENT
         | PIKCHR_PROCESS_DIV_CENTER
         | PIKCHR_PROCESS_DIV_FLOAT_RIGHT
         | PIKCHR_PROCESS_DIV_FLOAT_LEFT


         ) & pikFlags){
    pikFlags |= PIKCHR_PROCESS_DIV;
  }
  if(!(PIKCHR_PROCESS_TH1 & pikFlags)
     /* If any TH1_xxx flags are set, set TH1 */
     && (PIKCHR_PROCESS_TH1_NOSVG & pikFlags || thFlags!=0)){
    pikFlags |= PIKCHR_PROCESS_TH1;







>
>



|
>
|
|
|
|












|




|
|











|
>
>
>
>
>
>
>
>
>
>
>
>




|
>
>

|
|
|



|
>
>



|


|









>
>







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#define PIKCHR_PROCESS_SRC        0x10
#define PIKCHR_PROCESS_SRC_HIDDEN 0x20
#define PIKCHR_PROCESS_DIV        0x40
#define PIKCHR_PROCESS_DIV_INDENT      0x0100
#define PIKCHR_PROCESS_DIV_CENTER      0x0200
#define PIKCHR_PROCESS_DIV_FLOAT_LEFT  0x0400
#define PIKCHR_PROCESS_DIV_FLOAT_RIGHT 0x0800
#define PIKCHR_PROCESS_DIV_TOGGLE      0x1000
#define PIKCHR_PROCESS_DIV_SOURCE      0x2000
#endif

/*
** Processes a pikchr script, optionally with embedded TH1, and
** produces HTML code for it. zIn is the NUL-terminated input
** script. pikFlags may be a bitmask of any of the PIKCHR_PROCESS_xxx
** flags documented below. thFlags may be a bitmask of any of the
** TH_INIT_xxx and/or TH_R2B_xxx flags. Output is sent to pOut,
** appending to it without modifying any prior contents.
**
** Returns 0 on success, 1 if TH1 processing failed, or 2 if pikchr
** processing failed. In either case, the error message (if any) from
** TH1 or pikchr will be appended to pOut.
**
** pikFlags flag descriptions:
**
** - PIKCHR_PROCESS_TH1 means to run zIn through TH1, using the TH1
** init flags specified in the 3rd argument. If thFlags is non-0 then
** this flag is assumed even if it is not specified.
**
** - PIKCHR_PROCESS_TH1_NOSVG means that processing stops after the
** TH1 eval step, thus the output will be (presumably) a
** TH1-generated/processed pikchr script (or whatever else the TH1
** outputs). If this flag is set, PIKCHR_PROCESS_TH1 is assumed even
** if it is not specified.
**
** All of the remaining flags listed below are ignored if
** PIKCHR_PROCESS_TH1_NOSVG is specified!
**
** - PIKCHR_PROCESS_DIV: if set, the SVG result is wrapped in a DIV
** element which specifies a max-width style value based on the SVG's
** calculated size. This flag has multiple mutually exclusive forms:
**
**  - PIKCHR_PROCESS_DIV uses default element alignment.
**  - PIKCHR_PROCESS_DIV_INDENT indents the div.
**  - PIKCHR_PROCESS_DIV_CENTER centers the div.
**  - PIKCHR_PROCESS_DIV_FLOAT_LEFT floats the div left.
**  - PIKCHR_PROCESS_DIV_FLOAT_RIGHT floats the div right.
**
** If more than one is specified, which one is used is undefined. Those
** flags may be OR'd with one or both of the following:
**
**  - PIKCHR_PROCESS_DIV_TOGGLE: adds the 'toggle' CSS class to the
**    outer DIV so that event-handler code can install different
**    toggling behaviour than the default. Default is ctrl-click, but
**    this flag enables single-click toggling for the element.
**
**  - PIKCHR_PROCESS_DIV_SOURCE: adds the 'source' CSS class to the
**    outer DIV, which is a hint to the client-side renderer (see
**    fossil.pikchr.js) that the pikchr should initially be rendered
**    in source code form mode (the default is to hide the source and
**    show the SVG).
**
** - PIKCHR_PROCESS_NONCE: if set, the resulting SVG/DIV are wrapped
** in "safe nonce" comments, which are a fossil-internal mechanism
** which prevents the wiki/markdown processors from re-processing this
** output. This is necessary when calling this routine in the context
** of wiki/embedded doc processing, but not (e.g.) when fetching
** an image for /pikchrpage.
**
** - PIKCHR_PROCESS_SRC: if set, a new PRE.pikchr-src element is
** injected adjacent to the SVG element which contains the
** HTML-escaped content of the input script.
**
** - PIKCHR_PROCESS_SRC_HIDDEN: exactly like PIKCHR_PROCESS_SRC but
** the .pikchr-src tag also gets the CSS class 'hidden' (which, in
** fossil's default CSS, will hide that element). This is almost
** always what client code will want to do if it includes the source
** at all.
**
** - PIKCHR_PROCESS_ERR_PRE: if set and pikchr() fails, the resulting
** error report is wrapped in a PRE element, else it is retained
** as-is (intended only for console output).
*/
int pikchr_process(const char * zIn, int pikFlags, int thFlags,
                   Blob * pOut){
  Blob bIn = empty_blob;
  int isErr = 0;

  if(!(PIKCHR_PROCESS_DIV & pikFlags)
     /* If any DIV_xxx flags are set, set DIV */
     && (PIKCHR_PROCESS_DIV_INDENT
         | PIKCHR_PROCESS_DIV_CENTER
         | PIKCHR_PROCESS_DIV_FLOAT_RIGHT
         | PIKCHR_PROCESS_DIV_FLOAT_LEFT
         | PIKCHR_PROCESS_DIV_SOURCE
         | PIKCHR_PROCESS_DIV_TOGGLE
         ) & pikFlags){
    pikFlags |= PIKCHR_PROCESS_DIV;
  }
  if(!(PIKCHR_PROCESS_TH1 & pikFlags)
     /* If any TH1_xxx flags are set, set TH1 */
     && (PIKCHR_PROCESS_TH1_NOSVG & pikFlags || thFlags!=0)){
    pikFlags |= PIKCHR_PROCESS_TH1;
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
    }else{
      int w = 0, h = 0;
      const char * zContent = blob_str(&bIn);
      char *zOut;

      zOut = pikchr(zContent, "pikchr", 0, &w, &h);
      if( w>0 && h>0 ){


        const char *zNonce = (PIKCHR_PROCESS_NONCE & pikFlags)
          ? safe_html_nonce(1) : 0;
        if(zNonce){
          blob_append(pOut, zNonce, -1);
        }
        if(PIKCHR_PROCESS_DIV & pikFlags){
          Blob css = empty_blob;
          blob_appendf(&css, "max-width:%dpx;", w);
          if(PIKCHR_PROCESS_DIV_CENTER & pikFlags){
            blob_append(&css, "display:block;margin-auto;", -1);
          }else if(PIKCHR_PROCESS_DIV_INDENT & pikFlags){
            blob_append(&css, "margin-left:4em", -1);
          }else if(PIKCHR_PROCESS_DIV_FLOAT_LEFT & pikFlags){
            blob_append(&css, "float:left;padding=4em;", -1);
          }else if(PIKCHR_PROCESS_DIV_FLOAT_RIGHT & pikFlags){
            blob_append(&css, "float:right;padding=4em;", -1);
          }






          blob_appendf(pOut,"<div class=\"pikchr\" style=\"%b\">\n", &css);



          blob_reset(&css);
        }
        blob_append(pOut, zOut, -1);
        if((PIKCHR_PROCESS_SRC & pikFlags)
           || (PIKCHR_PROCESS_SRC_HIDDEN & pikFlags)){
          blob_appendf(pOut, "<textarea rows='10' readonly "
                       "class='pikchr-src%s'>"
                       "%h</textarea>\n",
                       (PIKCHR_PROCESS_SRC_HIDDEN & pikFlags)
                       ? " hidden" : "",
                       blob_str(&bIn));
        }
        if(PIKCHR_PROCESS_DIV & pikFlags){
          blob_append(pOut, "</div>\n", 7);
        }







>
>

















>
>
>
>
>
>
|
>
>
>





<
|
|







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191

192
193
194
195
196
197
198
199
200
    }else{
      int w = 0, h = 0;
      const char * zContent = blob_str(&bIn);
      char *zOut;

      zOut = pikchr(zContent, "pikchr", 0, &w, &h);
      if( w>0 && h>0 ){
        const char * zClassToggle = "";
        const char * zClassSource = "";
        const char *zNonce = (PIKCHR_PROCESS_NONCE & pikFlags)
          ? safe_html_nonce(1) : 0;
        if(zNonce){
          blob_append(pOut, zNonce, -1);
        }
        if(PIKCHR_PROCESS_DIV & pikFlags){
          Blob css = empty_blob;
          blob_appendf(&css, "max-width:%dpx;", w);
          if(PIKCHR_PROCESS_DIV_CENTER & pikFlags){
            blob_append(&css, "display:block;margin-auto;", -1);
          }else if(PIKCHR_PROCESS_DIV_INDENT & pikFlags){
            blob_append(&css, "margin-left:4em", -1);
          }else if(PIKCHR_PROCESS_DIV_FLOAT_LEFT & pikFlags){
            blob_append(&css, "float:left;padding=4em;", -1);
          }else if(PIKCHR_PROCESS_DIV_FLOAT_RIGHT & pikFlags){
            blob_append(&css, "float:right;padding=4em;", -1);
          }
          if(PIKCHR_PROCESS_DIV_TOGGLE & pikFlags){
            zClassToggle = " toggle";
          }
          if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){
            zClassSource = " source";
          }
          blob_appendf(pOut,"<div class=\"pikchr-svg%s%s\" "
                       "style=\"%b\">\n",
                       zClassToggle/*safe-for-%s*/,
                       zClassSource/*safe-for-%s*/, &css);
          blob_reset(&css);
        }
        blob_append(pOut, zOut, -1);
        if((PIKCHR_PROCESS_SRC & pikFlags)
           || (PIKCHR_PROCESS_SRC_HIDDEN & pikFlags)){

          blob_appendf(pOut, "<pre class='pikchr-src%s'>"
                       "%h</pre>\n",
                       (PIKCHR_PROCESS_SRC_HIDDEN & pikFlags)
                       ? " hidden" : "",
                       blob_str(&bIn));
        }
        if(PIKCHR_PROCESS_DIV & pikFlags){
          blob_append(pOut, "</div>\n", 7);
        }
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
**
**    -div-center Like -div but centers the div.
**
**    -div-left   Like -div but floats the div left.
**
**    -div-right  Like -div but floats the div right.
**
**    -svg-src   Stores the input pikchr's source code in the output as
**               a separate element adjacent to the SVG one. The
**               source element initially has the "hidden" CSS class.
**
**    -th        Process the input using TH1 before passing it to pikchr.
**
**    -th-novar  Disable $var and $<var> TH1 processing. Use this if the
**               pikchr script uses '$' for its own purposes and that







|







386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
**
**    -div-center Like -div but centers the div.
**
**    -div-left   Like -div but floats the div left.
**
**    -div-right  Like -div but floats the div right.
**
**    -src       Stores the input pikchr's source code in the output as
**               a separate element adjacent to the SVG one. The
**               source element initially has the "hidden" CSS class.
**
**    -th        Process the input using TH1 before passing it to pikchr.
**
**    -th-novar  Disable $var and $<var> TH1 processing. Use this if the
**               pikchr script uses '$' for its own purposes and that
397
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
  Blob bIn = empty_blob;
  Blob bOut = empty_blob;
  const char * zInfile = "-";
  const char * zOutfile = "-";
  const int fTh1 = find_option("th",0,0)!=0;
  const int fNosvg = find_option("th-nosvg",0,0)!=0;
  int isErr = 0;
  int pikFlags = find_option("svg-src",0,0)!=0
    ? PIKCHR_PROCESS_SRC_HIDDEN : 0;
  u32 fThFlags = TH_INIT_NO_ENCODE
    | (find_option("th-novar",0,0)!=0 ? TH_R2B_NO_VARS : 0);

  Th_InitTraceLog()/*processes -th-trace flag*/;

  if(find_option("div",0,0)!=0){
    pikFlags |= PIKCHR_PROCESS_DIV;
  }else if(find_option("div-indent",0,0)!=0){
    pikFlags |= PIKCHR_PROCESS_DIV_INDENT;
  }else if(find_option("div-center",0,0)!=0){
    pikFlags |= PIKCHR_PROCESS_DIV_CENTER;
  }else if(find_option("div-float-left",0,0)!=0){
    pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_LEFT;
  }else if(find_option("div-float-right",0,0)!=0){
    pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_RIGHT;
  }







  verify_all_options();
  if(g.argc>4){
    usage("?INFILE? ?OUTFILE?");
  }
  if(g.argc>2){
    zInfile = g.argv[2];







|

















>
>
>
>
>
>







428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
  Blob bIn = empty_blob;
  Blob bOut = empty_blob;
  const char * zInfile = "-";
  const char * zOutfile = "-";
  const int fTh1 = find_option("th",0,0)!=0;
  const int fNosvg = find_option("th-nosvg",0,0)!=0;
  int isErr = 0;
  int pikFlags = find_option("src",0,0)!=0
    ? PIKCHR_PROCESS_SRC_HIDDEN : 0;
  u32 fThFlags = TH_INIT_NO_ENCODE
    | (find_option("th-novar",0,0)!=0 ? TH_R2B_NO_VARS : 0);

  Th_InitTraceLog()/*processes -th-trace flag*/;

  if(find_option("div",0,0)!=0){
    pikFlags |= PIKCHR_PROCESS_DIV;
  }else if(find_option("div-indent",0,0)!=0){
    pikFlags |= PIKCHR_PROCESS_DIV_INDENT;
  }else if(find_option("div-center",0,0)!=0){
    pikFlags |= PIKCHR_PROCESS_DIV_CENTER;
  }else if(find_option("div-float-left",0,0)!=0){
    pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_LEFT;
  }else if(find_option("div-float-right",0,0)!=0){
    pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_RIGHT;
  }
  if(find_option("div-toggle",0,0)!=0){
    pikFlags |= PIKCHR_PROCESS_DIV_TOGGLE;
  }
  if(find_option("div-source",0,0)!=0){
    pikFlags |= PIKCHR_PROCESS_DIV_SOURCE;
  }

  verify_all_options();
  if(g.argc>4){
    usage("?INFILE? ?OUTFILE?");
  }
  if(g.argc>2){
    zInfile = g.argv[2];