Fossil SCM

Implemented most of the feedback from [https://pikchr.org/home/forumpost/f538d8e7f7], with the exceptions being documented in that thread. /pikchrshow now longer uses the mode-swap feature (seems superfluous there) and tapping anywhere in the body, outside of the source/svg container element, now turns off those buttons on all pikchrs where they are currently visible. Consolidated pikchr-rendering impl for markdown/fossil/pikchrshow/pikchr command.

stephan 2020-09-17 16:31 trunk
Commit 4f697731832a44b4dc5bd129152324323e17b14a6498284e88cd089b8b9d5d75
--- src/default.css
+++ src/default.css
@@ -1385,6 +1385,12 @@
13851385
13861386
noscript > .error {
13871387
/* Part of the style_emit_noscript_for_js_page() interface. */
13881388
padding: 1em;
13891389
font-size: 150%;
1390
+}
1391
+.pikchr-src { /* source code view for a pikchr (see fossil.pikchr.js) */
1392
+ box-sizing: border-box/*reduces UI shift*/;
1393
+ border-width: 1px;
1394
+ border-style: dotted;
1395
+ overflow: auto;
13901396
}
13911397
--- src/default.css
+++ src/default.css
@@ -1385,6 +1385,12 @@
1385
1386 noscript > .error {
1387 /* Part of the style_emit_noscript_for_js_page() interface. */
1388 padding: 1em;
1389 font-size: 150%;
 
 
 
 
 
 
1390 }
1391
--- src/default.css
+++ src/default.css
@@ -1385,6 +1385,12 @@
1385
1386 noscript > .error {
1387 /* Part of the style_emit_noscript_for_js_page() interface. */
1388 padding: 1em;
1389 font-size: 150%;
1390 }
1391 .pikchr-src { /* source code view for a pikchr (see fossil.pikchr.js) */
1392 box-sizing: border-box/*reduces UI shift*/;
1393 border-width: 1px;
1394 border-style: dotted;
1395 overflow: auto;
1396 }
1397
--- src/fossil.page.pikchrshow.js
+++ src/fossil.page.pikchrshow.js
@@ -326,13 +326,10 @@
326326
D.parseHtml(D.clearElement(preTgt), P.response.raw);
327327
svg = f.getSvgNode(this.response.raw);
328328
if(svg){ /*for copy button*/
329329
this.e.taPreviewText.value = svg.outerHTML;
330330
}
331
- if(F.pikchr){
332
- F.pikchr.addSrcView(preTgt.querySelector('svg'));
333
- }
334331
break;
335332
case 1:
336333
label = "Markdown";
337334
f.showMarkupAlignment(true);
338335
this.e.taPreviewText.value = [
339336
--- src/fossil.page.pikchrshow.js
+++ src/fossil.page.pikchrshow.js
@@ -326,13 +326,10 @@
326 D.parseHtml(D.clearElement(preTgt), P.response.raw);
327 svg = f.getSvgNode(this.response.raw);
328 if(svg){ /*for copy button*/
329 this.e.taPreviewText.value = svg.outerHTML;
330 }
331 if(F.pikchr){
332 F.pikchr.addSrcView(preTgt.querySelector('svg'));
333 }
334 break;
335 case 1:
336 label = "Markdown";
337 f.showMarkupAlignment(true);
338 this.e.taPreviewText.value = [
339
--- src/fossil.page.pikchrshow.js
+++ src/fossil.page.pikchrshow.js
@@ -326,13 +326,10 @@
326 D.parseHtml(D.clearElement(preTgt), P.response.raw);
327 svg = f.getSvgNode(this.response.raw);
328 if(svg){ /*for copy button*/
329 this.e.taPreviewText.value = svg.outerHTML;
330 }
 
 
 
331 break;
332 case 1:
333 label = "Markdown";
334 f.showMarkupAlignment(true);
335 this.e.taPreviewText.value = [
336
--- src/fossil.pikchr.js
+++ src/fossil.pikchr.js
@@ -12,17 +12,22 @@
1212
(function(){
1313
const head = document.head || document.querySelector('head'),
1414
styleTag = document.createElement('style'),
1515
wh = '1cm' /* fixed width/height of buttons */,
1616
styleCSS = `
17
+.pikchr-button-bar {
18
+ position: absolute;
19
+ position: absolute;
20
+ top: 0;
21
+ left: 0;
22
+ display: inline-flex;
23
+ flex-direction: column;
24
+}
1725
.pikchr-src-button {
1826
min-height: ${wh}; max-height: ${wh};
1927
min-width: ${wh}; max-width: ${wh};
2028
font-size: ${wh};
21
- position: absolute;
22
- top: 0;
23
- left: 0;
2429
border: 1px solid black;
2530
background-color: rgba(255,255,0,0.7);
2631
border-radius: 0.25cm;
2732
z-index: 50;
2833
cursor: pointer;
@@ -116,10 +121,16 @@
116121
it is passed to this function multiple times. Each processed
117122
element gets a "data" attribute set to it to indicate that it was
118123
already dealt with.
119124
*/
120125
P.addSrcView = function f(svg,opt){
126
+ if(!f.hasOwnProperty('bodyClick')){
127
+ f.bodyClick = function(){
128
+ D.addClass(document.querySelectorAll('.pikchr-button-bar'), 'hidden');
129
+ };
130
+ document.body.addEventListener('click', f.bodyClick, false);
131
+ }
121132
if(!svg) svg = 'svg.pikchr';
122133
if('string' === typeof svg){
123134
document.querySelectorAll(svg).forEach(
124135
(e)=>f.call(this, e, opt)
125136
);
@@ -130,59 +141,34 @@
130141
}
131142
if(svg.dataset.pikchrProcessed){
132143
return this;
133144
}
134145
svg.dataset.pikchrProcessed = 1;
135
- const src = svg.querySelector('pikchr\\:src');
136
- if(!src){
137
- console.warn("No pikchr:src node found in",svg);
146
+ const parent = svg.parentNode;
147
+ parent.style.position = 'relative' /* REQUIRED for btn placement */;
148
+ const srcView = parent.querySelector('.pikchr-src');
149
+ if(!srcView){
150
+ console.warn("No pikchr source node found in",parent);
138151
return this;
139152
}
140
- opt = F.mergeLastWins({
141
- },opt);
142
- const parent = svg.parentNode;
143
- parent.style.position = 'relative' /* REQUIRED for btn placement */;
144
- const srcView = D.addClass(D.textarea(0,0,true), 'pikchr-src-text');
145
- srcView.value = src.textContent;
153
+ const buttonBar = D.addClass(D.span(), 'pikchr-button-bar');
146154
const btnFlip = D.append(
147155
D.addClass(D.span(), 'pikchr-src-button'),
148156
);
149
- const btnCopy = F.copyButton(
150
- D.span(), {
151
- cssClass: ['copy-button', 'pikchr-copy-button'],
152
- extractText: function(){
153
- return (srcView.classList.contains('hidden')
154
- ? svg.outerHTML
155
- : srcView.value);
156
- }
157
- }
158
- );
159
- const buttons = [btnFlip, btnCopy];
160
- D.addClass(buttons, 'hidden');
161
- D.append(parent, D.addClass(srcView, 'hidden'), buttons);
162
-
163
- /**
164
- Toggle the buttons on only when the mouse is in the parent
165
- widget's area or the user taps on that area. This seems much
166
- less "busy" than having them always visible and slightly in the way.
167
- It also means that we can make them a bit larger.
168
- */
169
- if(0){ /* Mouse enter/leave triggers currently disabled by request */
170
- parent.addEventListener('mouseenter', function(ev){
171
- if(ev.target === parent) D.removeClass(buttons, 'hidden');
172
- }, true);
173
- parent.addEventListener('mouseleave', function(ev){
174
- if(ev.target === parent) D.addClass(buttons, 'hidden');
175
- }, true);
176
- /* mouseenter/leave work well... but only if there's a mouse. */
177
- }
178
- parent.addEventListener('click', function(ev){
157
+ D.append(buttonBar, btnFlip);
158
+ // not yet sure which options we can/should support:
159
+ // opt = F.mergeLastWins({},opt);
160
+ D.addClass(srcView, 'hidden')/*should already be so, but just in case*/;
161
+ D.append(parent, D.addClass(buttonBar, 'hidden'));
162
+
163
+ parent.addEventListener('click', function f(ev){
179164
ev.preventDefault();
180165
ev.stopPropagation();
181
- D.toggleClass(buttons, 'hidden');
166
+ D.toggleClass(buttonBar, 'hidden');
182167
}, false);
183168
169
+ /** Toggle the source/SVG view on click. */
184170
btnFlip.addEventListener('click', function f(ev){
185171
ev.preventDefault();
186172
ev.stopPropagation();
187173
if(!f.hasOwnProperty('origMaxWidth')){
188174
f.origMaxWidth = parent.style.maxWidth;
@@ -215,11 +201,11 @@
215201
parent.style.maxWidth = 'unset';
216202
}else{/*srcView is active*/
217203
parent.style.maxWidth = f.origMaxWidth;
218204
parent.style.width = 'unset';
219205
}
220
- }else{
206
+ }else if(1){
221207
/* Option #2: gives us good results for non-centered items but
222208
not for centered. We apparently have no(?) reliable way of
223209
distinguishing centered from left/indented pikchrs here
224210
unless we add a CSS class to mark them as such in the
225211
pikchr-to-wiki-image code. */
@@ -228,13 +214,12 @@
228214
parent.style.maxWidth = 'unset';
229215
}else{/*srcView is active*/
230216
parent.style.maxWidth = f.origMaxWidth;
231217
parent.style.width = 'unset';
232218
}
233
-
234219
}
235220
btnFlip.classList.toggle('src-active');
236221
D.toggleClass([svg, srcView], 'hidden');
237222
}, false);
238223
};
239224
240225
})(window.fossil);
241226
--- src/fossil.pikchr.js
+++ src/fossil.pikchr.js
@@ -12,17 +12,22 @@
12 (function(){
13 const head = document.head || document.querySelector('head'),
14 styleTag = document.createElement('style'),
15 wh = '1cm' /* fixed width/height of buttons */,
16 styleCSS = `
 
 
 
 
 
 
 
 
17 .pikchr-src-button {
18 min-height: ${wh}; max-height: ${wh};
19 min-width: ${wh}; max-width: ${wh};
20 font-size: ${wh};
21 position: absolute;
22 top: 0;
23 left: 0;
24 border: 1px solid black;
25 background-color: rgba(255,255,0,0.7);
26 border-radius: 0.25cm;
27 z-index: 50;
28 cursor: pointer;
@@ -116,10 +121,16 @@
116 it is passed to this function multiple times. Each processed
117 element gets a "data" attribute set to it to indicate that it was
118 already dealt with.
119 */
120 P.addSrcView = function f(svg,opt){
 
 
 
 
 
 
121 if(!svg) svg = 'svg.pikchr';
122 if('string' === typeof svg){
123 document.querySelectorAll(svg).forEach(
124 (e)=>f.call(this, e, opt)
125 );
@@ -130,59 +141,34 @@
130 }
131 if(svg.dataset.pikchrProcessed){
132 return this;
133 }
134 svg.dataset.pikchrProcessed = 1;
135 const src = svg.querySelector('pikchr\\:src');
136 if(!src){
137 console.warn("No pikchr:src node found in",svg);
 
 
138 return this;
139 }
140 opt = F.mergeLastWins({
141 },opt);
142 const parent = svg.parentNode;
143 parent.style.position = 'relative' /* REQUIRED for btn placement */;
144 const srcView = D.addClass(D.textarea(0,0,true), 'pikchr-src-text');
145 srcView.value = src.textContent;
146 const btnFlip = D.append(
147 D.addClass(D.span(), 'pikchr-src-button'),
148 );
149 const btnCopy = F.copyButton(
150 D.span(), {
151 cssClass: ['copy-button', 'pikchr-copy-button'],
152 extractText: function(){
153 return (srcView.classList.contains('hidden')
154 ? svg.outerHTML
155 : srcView.value);
156 }
157 }
158 );
159 const buttons = [btnFlip, btnCopy];
160 D.addClass(buttons, 'hidden');
161 D.append(parent, D.addClass(srcView, 'hidden'), buttons);
162
163 /**
164 Toggle the buttons on only when the mouse is in the parent
165 widget's area or the user taps on that area. This seems much
166 less "busy" than having them always visible and slightly in the way.
167 It also means that we can make them a bit larger.
168 */
169 if(0){ /* Mouse enter/leave triggers currently disabled by request */
170 parent.addEventListener('mouseenter', function(ev){
171 if(ev.target === parent) D.removeClass(buttons, 'hidden');
172 }, true);
173 parent.addEventListener('mouseleave', function(ev){
174 if(ev.target === parent) D.addClass(buttons, 'hidden');
175 }, true);
176 /* mouseenter/leave work well... but only if there's a mouse. */
177 }
178 parent.addEventListener('click', function(ev){
179 ev.preventDefault();
180 ev.stopPropagation();
181 D.toggleClass(buttons, 'hidden');
182 }, false);
183
 
184 btnFlip.addEventListener('click', function f(ev){
185 ev.preventDefault();
186 ev.stopPropagation();
187 if(!f.hasOwnProperty('origMaxWidth')){
188 f.origMaxWidth = parent.style.maxWidth;
@@ -215,11 +201,11 @@
215 parent.style.maxWidth = 'unset';
216 }else{/*srcView is active*/
217 parent.style.maxWidth = f.origMaxWidth;
218 parent.style.width = 'unset';
219 }
220 }else{
221 /* Option #2: gives us good results for non-centered items but
222 not for centered. We apparently have no(?) reliable way of
223 distinguishing centered from left/indented pikchrs here
224 unless we add a CSS class to mark them as such in the
225 pikchr-to-wiki-image code. */
@@ -228,13 +214,12 @@
228 parent.style.maxWidth = 'unset';
229 }else{/*srcView is active*/
230 parent.style.maxWidth = f.origMaxWidth;
231 parent.style.width = 'unset';
232 }
233
234 }
235 btnFlip.classList.toggle('src-active');
236 D.toggleClass([svg, srcView], 'hidden');
237 }, false);
238 };
239
240 })(window.fossil);
241
--- src/fossil.pikchr.js
+++ src/fossil.pikchr.js
@@ -12,17 +12,22 @@
12 (function(){
13 const head = document.head || document.querySelector('head'),
14 styleTag = document.createElement('style'),
15 wh = '1cm' /* fixed width/height of buttons */,
16 styleCSS = `
17 .pikchr-button-bar {
18 position: absolute;
19 position: absolute;
20 top: 0;
21 left: 0;
22 display: inline-flex;
23 flex-direction: column;
24 }
25 .pikchr-src-button {
26 min-height: ${wh}; max-height: ${wh};
27 min-width: ${wh}; max-width: ${wh};
28 font-size: ${wh};
 
 
 
29 border: 1px solid black;
30 background-color: rgba(255,255,0,0.7);
31 border-radius: 0.25cm;
32 z-index: 50;
33 cursor: pointer;
@@ -116,10 +121,16 @@
121 it is passed to this function multiple times. Each processed
122 element gets a "data" attribute set to it to indicate that it was
123 already dealt with.
124 */
125 P.addSrcView = function f(svg,opt){
126 if(!f.hasOwnProperty('bodyClick')){
127 f.bodyClick = function(){
128 D.addClass(document.querySelectorAll('.pikchr-button-bar'), 'hidden');
129 };
130 document.body.addEventListener('click', f.bodyClick, false);
131 }
132 if(!svg) svg = 'svg.pikchr';
133 if('string' === typeof svg){
134 document.querySelectorAll(svg).forEach(
135 (e)=>f.call(this, e, opt)
136 );
@@ -130,59 +141,34 @@
141 }
142 if(svg.dataset.pikchrProcessed){
143 return this;
144 }
145 svg.dataset.pikchrProcessed = 1;
146 const parent = svg.parentNode;
147 parent.style.position = 'relative' /* REQUIRED for btn placement */;
148 const srcView = parent.querySelector('.pikchr-src');
149 if(!srcView){
150 console.warn("No pikchr source node found in",parent);
151 return this;
152 }
153 const buttonBar = D.addClass(D.span(), 'pikchr-button-bar');
 
 
 
 
 
154 const btnFlip = D.append(
155 D.addClass(D.span(), 'pikchr-src-button'),
156 );
157 D.append(buttonBar, btnFlip);
158 // not yet sure which options we can/should support:
159 // opt = F.mergeLastWins({},opt);
160 D.addClass(srcView, 'hidden')/*should already be so, but just in case*/;
161 D.append(parent, D.addClass(buttonBar, 'hidden'));
162
163 parent.addEventListener('click', function f(ev){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164 ev.preventDefault();
165 ev.stopPropagation();
166 D.toggleClass(buttonBar, 'hidden');
167 }, false);
168
169 /** Toggle the source/SVG view on click. */
170 btnFlip.addEventListener('click', function f(ev){
171 ev.preventDefault();
172 ev.stopPropagation();
173 if(!f.hasOwnProperty('origMaxWidth')){
174 f.origMaxWidth = parent.style.maxWidth;
@@ -215,11 +201,11 @@
201 parent.style.maxWidth = 'unset';
202 }else{/*srcView is active*/
203 parent.style.maxWidth = f.origMaxWidth;
204 parent.style.width = 'unset';
205 }
206 }else if(1){
207 /* Option #2: gives us good results for non-centered items but
208 not for centered. We apparently have no(?) reliable way of
209 distinguishing centered from left/indented pikchrs here
210 unless we add a CSS class to mark them as such in the
211 pikchr-to-wiki-image code. */
@@ -228,13 +214,12 @@
214 parent.style.maxWidth = 'unset';
215 }else{/*srcView is active*/
216 parent.style.maxWidth = f.origMaxWidth;
217 parent.style.width = 'unset';
218 }
 
219 }
220 btnFlip.classList.toggle('src-active');
221 D.toggleClass([svg, srcView], 'hidden');
222 }, false);
223 };
224
225 })(window.fossil);
226
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -342,50 +342,39 @@
342342
void pikchr_to_html(
343343
Blob *ob, /* Write the generated SVG here */
344344
const char *zSrc, int nSrc, /* The Pikchr source text */
345345
const char *zArg, int nArg /* Addition arguments */
346346
){
347
- int w = 0, h = 0;
348
- char *zIn = fossil_strndup(zSrc, nSrc);
349
- char *zOut = pikchr(zIn, "pikchr", PIKCHR_INCLUDE_SOURCE, &w, &h);
350
- fossil_free(zIn);
351
- if( w>0 && h>0 ){
352
- const char *zNonce = safe_html_nonce(1);
353
- Blob css;
354
- blob_init(&css,0,0);
355
- blob_appendf(&css,"max-width:%dpx;",w);
356
- blob_append(ob, zNonce, -1);
357
- blob_append_char(ob, '\n');
358
- while( nArg>0 ){
359
- int i;
360
- for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){}
361
- if( i==6 && strncmp(zArg, "center", 6)==0 ){
362
- blob_appendf(&css, "display:block;margin:auto;");
363
- break;
364
- }else if( i==6 && strncmp(zArg, "indent", 6)==0 ){
365
- blob_appendf(&css, "margin-left:4em;");
366
- break;
367
- }else if( i==10 && strncmp(zArg, "float-left", 10)==0 ){
368
- blob_appendf(&css, "float:left;padding=4em;");
369
- break;
370
- }else if( i==11 && strncmp(zArg, "float-right", 11)==0 ){
371
- blob_appendf(&css, "float:right;padding=4em;");
372
- break;
373
- }
374
- while( i<nArg && fossil_isspace(zArg[i]) ){ i++; }
375
- zArg += i;
376
- nArg -= i;
377
- }
378
- blob_appendf(ob, "<div style='%s'>\n", blob_str(&css));
379
- blob_append(ob, zOut, -1);
380
- blob_appendf(ob, "</div>\n");
381
- blob_reset(&css);
382
- blob_appendf(ob, "%s\n", zNonce);
383
- }else{
384
- blob_appendf(ob, "<pre>\n%s\n</pre>\n", zOut);
385
- }
386
- free(zOut);
347
+ int pikFlags = PIKCHR_PROCESS_NONCE
348
+ | PIKCHR_PROCESS_DIV
349
+ | PIKCHR_PROCESS_SRC_HIDDEN;
350
+ Blob bSrc = empty_blob;
351
+
352
+ while( nArg>0 ){
353
+ int i;
354
+ for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){}
355
+ if( i==6 && strncmp(zArg, "center", 6)==0 ){
356
+ pikFlags |= PIKCHR_PROCESS_DIV_CENTER;
357
+ break;
358
+ }else if( i==6 && strncmp(zArg, "indent", 6)==0 ){
359
+ pikFlags |= PIKCHR_PROCESS_DIV_INDENT;
360
+ break;
361
+ }else if( i==10 && strncmp(zArg, "float-left", 10)==0 ){
362
+ pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_LEFT;
363
+ break;
364
+ }else if( i==11 && strncmp(zArg, "float-right", 11)==0 ){
365
+ pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_RIGHT;
366
+ break;
367
+ }
368
+ while( i<nArg && fossil_isspace(zArg[i]) ){ i++; }
369
+ zArg += i;
370
+ nArg -= i;
371
+ }
372
+ blob_append(&bSrc, zSrc, nSrc)
373
+ /*have to dupe input to ensure a NUL-terminated source string */;
374
+ pikchr_process(blob_str(&bSrc), pikFlags, 0, ob);
375
+ blob_reset(&bSrc);
387376
}
388377
389378
390379
/* Invoked for `...` blocks where there are nSep grave accents in a
391380
** row that serve as the delimiter. According to CommonMark:
392381
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -342,50 +342,39 @@
342 void pikchr_to_html(
343 Blob *ob, /* Write the generated SVG here */
344 const char *zSrc, int nSrc, /* The Pikchr source text */
345 const char *zArg, int nArg /* Addition arguments */
346 ){
347 int w = 0, h = 0;
348 char *zIn = fossil_strndup(zSrc, nSrc);
349 char *zOut = pikchr(zIn, "pikchr", PIKCHR_INCLUDE_SOURCE, &w, &h);
350 fossil_free(zIn);
351 if( w>0 && h>0 ){
352 const char *zNonce = safe_html_nonce(1);
353 Blob css;
354 blob_init(&css,0,0);
355 blob_appendf(&css,"max-width:%dpx;",w);
356 blob_append(ob, zNonce, -1);
357 blob_append_char(ob, '\n');
358 while( nArg>0 ){
359 int i;
360 for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){}
361 if( i==6 && strncmp(zArg, "center", 6)==0 ){
362 blob_appendf(&css, "display:block;margin:auto;");
363 break;
364 }else if( i==6 && strncmp(zArg, "indent", 6)==0 ){
365 blob_appendf(&css, "margin-left:4em;");
366 break;
367 }else if( i==10 && strncmp(zArg, "float-left", 10)==0 ){
368 blob_appendf(&css, "float:left;padding=4em;");
369 break;
370 }else if( i==11 && strncmp(zArg, "float-right", 11)==0 ){
371 blob_appendf(&css, "float:right;padding=4em;");
372 break;
373 }
374 while( i<nArg && fossil_isspace(zArg[i]) ){ i++; }
375 zArg += i;
376 nArg -= i;
377 }
378 blob_appendf(ob, "<div style='%s'>\n", blob_str(&css));
379 blob_append(ob, zOut, -1);
380 blob_appendf(ob, "</div>\n");
381 blob_reset(&css);
382 blob_appendf(ob, "%s\n", zNonce);
383 }else{
384 blob_appendf(ob, "<pre>\n%s\n</pre>\n", zOut);
385 }
386 free(zOut);
387 }
388
389
390 /* Invoked for `...` blocks where there are nSep grave accents in a
391 ** row that serve as the delimiter. According to CommonMark:
392
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -342,50 +342,39 @@
342 void pikchr_to_html(
343 Blob *ob, /* Write the generated SVG here */
344 const char *zSrc, int nSrc, /* The Pikchr source text */
345 const char *zArg, int nArg /* Addition arguments */
346 ){
347 int pikFlags = PIKCHR_PROCESS_NONCE
348 | PIKCHR_PROCESS_DIV
349 | PIKCHR_PROCESS_SRC_HIDDEN;
350 Blob bSrc = empty_blob;
351
352 while( nArg>0 ){
353 int i;
354 for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){}
355 if( i==6 && strncmp(zArg, "center", 6)==0 ){
356 pikFlags |= PIKCHR_PROCESS_DIV_CENTER;
357 break;
358 }else if( i==6 && strncmp(zArg, "indent", 6)==0 ){
359 pikFlags |= PIKCHR_PROCESS_DIV_INDENT;
360 break;
361 }else if( i==10 && strncmp(zArg, "float-left", 10)==0 ){
362 pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_LEFT;
363 break;
364 }else if( i==11 && strncmp(zArg, "float-right", 11)==0 ){
365 pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_RIGHT;
366 break;
367 }
368 while( i<nArg && fossil_isspace(zArg[i]) ){ i++; }
369 zArg += i;
370 nArg -= i;
371 }
372 blob_append(&bSrc, zSrc, nSrc)
373 /*have to dupe input to ensure a NUL-terminated source string */;
374 pikchr_process(blob_str(&bSrc), pikFlags, 0, ob);
375 blob_reset(&bSrc);
 
 
 
 
 
 
 
 
 
 
 
376 }
377
378
379 /* Invoked for `...` blocks where there are nSep grave accents in a
380 ** row that serve as the delimiter. According to CommonMark:
381
+101 -36
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -20,18 +20,23 @@
2020
#include "config.h"
2121
#include <assert.h>
2222
#include <ctype.h>
2323
#include "pikchrshow.h"
2424
25
-#ifdef INTERFACE
25
+#if INTERFACE
2626
/* These are described in pikchr_process()'s docs. */
2727
#define PIKCHR_PROCESS_TH1 0x01
2828
#define PIKCHR_PROCESS_TH1_NOSVG 0x02
29
-#define PIKCHR_PROCESS_DIV 0x04
30
-#define PIKCHR_PROCESS_NONCE 0x08
31
-#define PIKCHR_PROCESS_ERR_PRE 0x10
32
-#define PIKCHR_PROCESS_NO_SRC 0x20
29
+#define PIKCHR_PROCESS_NONCE 0x04
30
+#define PIKCHR_PROCESS_ERR_PRE 0x08
31
+#define PIKCHR_PROCESS_SRC 0x10
32
+#define PIKCHR_PROCESS_SRC_HIDDEN 0x20
33
+#define PIKCHR_PROCESS_DIV 0x40
34
+#define PIKCHR_PROCESS_DIV_INDENT 0x0100
35
+#define PIKCHR_PROCESS_DIV_CENTER 0x0200
36
+#define PIKCHR_PROCESS_DIV_FLOAT_LEFT 0x0400
37
+#define PIKCHR_PROCESS_DIV_FLOAT_RIGHT 0x0800
3338
#endif
3439
3540
/*
3641
** Processes a pikchr script, optionally with embedded TH1. zIn is the
3742
** input script. pikFlags may be a bitmask of any of the
@@ -49,38 +54,62 @@
4954
** init flags specified in the 3rd argument. If thFlags is non-0 then
5055
** this flag is assumed even if it is not specified.
5156
**
5257
** - PIKCHR_PROCESS_TH1_NOSVG means that processing stops after the
5358
** TH1 step, thus the output will be (presumably) a
54
-** TH1-generated/processed pikchr script, and not an SVG. If this flag
55
-** is set, PIKCHR_PROCESS_TH1 is assumed even if it is not specified.
59
+** TH1-generated/processed pikchr script (or whatever else the TH1
60
+** outputs). If this flag is set, PIKCHR_PROCESS_TH1 is assumed even
61
+** if it is not specified.
5662
**
5763
** The remaining flags listed below are ignored if
5864
** PIKCHR_PROCESS_TH1_NOSVG is specified:
5965
**
60
-** - PIKCHR_PROCESS_NO_SRC: by default the contents of zIn are stored
61
-** in the resulting SVG content, as part of the image metadata. That
62
-** is suppressed if this flag is set.
63
-**
6466
** - PIKCHR_PROCESS_DIV: if set, the SVG result is wrapped in a DIV
6567
** element which specifies a max-width style value based on the SVG's
66
-** calculated size.
68
+** calculated size. This flag has multiple mutually exclusive forms:
69
+**
70
+** - PIKCHR_PROCESS_DIV uses default element alignment.
71
+** - PIKCHR_PROCESS_DIV_INDENT indents the div.
72
+** - PIKCHR_PROCESS_DIV_CENTER centers the div.
73
+** - PIKCHR_PROCESS_DIV_FLOAT_LEFT floats the div left.
74
+** - PIKCHR_PROCESS_DIV_FLOAT_RIGHT floats the div right.
75
+**
76
+** If more than one is specified, which one is used is undefined.
6777
**
68
-** - PIKCHR_PROCESS_NONCE: if set, the resulting SVG/DEV are wrapped
78
+** - PIKCHR_PROCESS_NONCE: if set, the resulting SVG/DIV are wrapped
6979
** in "safe nonce" comments, which are a fossil-internal mechanism
70
-** which prevents the wiki/markdown processors from processing this
80
+** which prevents the wiki/markdown processors from re-processing this
7181
** output.
82
+**
83
+** - PIKCHR_PROCESS_SRC: if set, a new TEXTAREA.pikchr-src element is injected
84
+** adjacet to the SVG element which contains the HTML-escaped content of
85
+** the input script.
86
+**
87
+** - PIKCHR_PROCESS_SRC_HIDDEN: exactly like PIKCHR_PROCESS_SRC but
88
+** the .pikchr-src tag also gets the CSS class 'hidden' (which, in
89
+** fossil's default CSS, will hide that element).
7290
**
7391
** - PIKCHR_PROCESS_ERR_PRE: if set and pikchr() fails, the resulting
74
-** error report is wrapped in PRE element.
92
+** error report is wrapped in a PRE element, else it is retained
93
+** as-is (intended for console output).
7594
*/
7695
int pikchr_process(const char * zIn, int pikFlags, int thFlags,
77
- Blob * pOut){
96
+ Blob * pOut){
7897
Blob bIn = empty_blob;
7998
int isErr = 0;
8099
100
+ if(!(PIKCHR_PROCESS_DIV & pikFlags)
101
+ /* If any DIV_xxx flags are set, set DIV */
102
+ && (PIKCHR_PROCESS_DIV_INDENT
103
+ | PIKCHR_PROCESS_DIV_CENTER
104
+ | PIKCHR_PROCESS_DIV_FLOAT_RIGHT
105
+ | PIKCHR_PROCESS_DIV_FLOAT_LEFT
106
+ ) & pikFlags){
107
+ pikFlags |= PIKCHR_PROCESS_DIV;
108
+ }
81109
if(!(PIKCHR_PROCESS_TH1 & pikFlags)
110
+ /* If any TH1_xxx flags are set, set TH1 */
82111
&& (PIKCHR_PROCESS_TH1_NOSVG & pikFlags || thFlags!=0)){
83112
pikFlags |= PIKCHR_PROCESS_TH1;
84113
}
85114
if(PIKCHR_PROCESS_TH1 & pikFlags){
86115
Blob out = empty_blob;
@@ -100,26 +129,45 @@
100129
blob_append(pOut, blob_str(&bIn), blob_size(&bIn));
101130
}else{
102131
int w = 0, h = 0;
103132
const char * zContent = blob_str(&bIn);
104133
char *zOut;
105
- const unsigned int pikFlags2 = (PIKCHR_PROCESS_NO_SRC & pikFlags)
106
- ? 0 : PIKCHR_INCLUDE_SOURCE;
107
-
108
- zOut = pikchr(zContent, "pikchr", pikFlags2, &w, &h);
134
+
135
+ zOut = pikchr(zContent, "pikchr", 0, &w, &h);
109136
if( w>0 && h>0 ){
110137
const char *zNonce = (PIKCHR_PROCESS_NONCE & pikFlags)
111138
? safe_html_nonce(1) : 0;
112139
if(zNonce){
113140
blob_append(pOut, zNonce, -1);
114141
}
115142
if(PIKCHR_PROCESS_DIV & pikFlags){
116
- blob_appendf(pOut,"<div style='max-width:%dpx;'>\n", w);
143
+ Blob css = empty_blob;
144
+ blob_appendf(&css, "max-width:%dpx;", w);
145
+ if(PIKCHR_PROCESS_DIV_CENTER & pikFlags){
146
+ blob_append(&css, "display:block;margin-auto;", -1);
147
+ }else if(PIKCHR_PROCESS_DIV_INDENT & pikFlags){
148
+ blob_append(&css, "margin-left:4em", -1);
149
+ }else if(PIKCHR_PROCESS_DIV_FLOAT_LEFT & pikFlags){
150
+ blob_append(&css, "float:left;padding=4em;", -1);
151
+ }else if(PIKCHR_PROCESS_DIV_FLOAT_RIGHT & pikFlags){
152
+ blob_append(&css, "float:right;padding=4em;", -1);
153
+ }
154
+ blob_appendf(pOut,"<div class=\"pikchr\" style=\"%b\">\n", &css);
155
+ blob_reset(&css);
117156
}
118157
blob_append(pOut, zOut, -1);
158
+ if((PIKCHR_PROCESS_SRC & pikFlags)
159
+ || (PIKCHR_PROCESS_SRC_HIDDEN & pikFlags)){
160
+ blob_appendf(pOut, "<textarea rows='10' readonly "
161
+ "class='pikchr-src%s'>"
162
+ "%h</textarea>\n",
163
+ (PIKCHR_PROCESS_SRC_HIDDEN & pikFlags)
164
+ ? " hidden" : "",
165
+ blob_str(&bIn));
166
+ }
119167
if(PIKCHR_PROCESS_DIV & pikFlags){
120
- blob_append(pOut,"</div>\n", 7);
168
+ blob_append(pOut, "</div>\n", 7);
121169
}
122170
if(zNonce){
123171
blob_append(pOut, zNonce, -1);
124172
}
125173
}else{
@@ -150,10 +198,13 @@
150198
** value to pre-populate the editor with that code.
151199
*/
152200
void pikchrshow_page(void){
153201
const char *zContent = 0;
154202
int isDark; /* true if the current skin is "dark" */
203
+ int pikFlags = PIKCHR_PROCESS_DIV
204
+ | PIKCHR_PROCESS_SRC_HIDDEN
205
+ | PIKCHR_PROCESS_ERR_PRE;
155206
156207
login_check_credentials();
157208
if( !g.perm.RdWiki && !g.perm.Read && !g.perm.RdForum ){
158209
cgi_redirectf("%s/login?g=%s/pikchrshow", g.zTop, g.zTop);
159210
}
@@ -162,13 +213,11 @@
162213
/* Called from the JS-side preview updater. */
163214
cgi_set_content_type("text/html");
164215
if(zContent && *zContent){
165216
Blob out = empty_blob;
166217
const int isErr =
167
- pikchr_process(zContent,
168
- PIKCHR_PROCESS_DIV | PIKCHR_PROCESS_ERR_PRE,
169
- 0, &out);
218
+ pikchr_process(zContent, pikFlags, 0, &out);
170219
if(isErr){
171220
cgi_printf_header("x-pikchrshow-is-error: %d\r\n", isErr);
172221
}
173222
CX("%b", &out);
174223
blob_reset(&out);
@@ -268,12 +317,11 @@
268317
/* Reminder: Firefox does not properly flexbox a LEGEND
269318
element, always flowing it in column mode. */);
270319
CX("<div id='pikchrshow-output'>");
271320
if(*zContent){
272321
Blob out = empty_blob;
273
- pikchr_process(zContent, PIKCHR_PROCESS_ERR_PRE
274
- | PIKCHR_PROCESS_DIV, 0, &out);
322
+ pikchr_process(zContent, pikFlags, 0, &out);
275323
CX("%b", &out);
276324
blob_reset(&out);
277325
} CX("</div>"/*#pikchrshow-output*/);
278326
} CX("</fieldset>"/*#pikchrshow-output-wrapper*/);
279327
} CX("</div>"/*sbs-wrapper*/);
@@ -299,15 +347,23 @@
299347
**
300348
** Options:
301349
**
302350
** -div On success, adds a DIV wrapper around the
303351
** resulting SVG output which limits its max-width to
304
-** its computed maximum ideal size, in order to mimic
305
-** how fossil's web-based components work.
352
+** its computed maximum ideal size.
353
+**
354
+** -div-indent Like -div but indents the div.
355
+**
356
+** -div-center Like -div but centers the div.
357
+**
358
+** -div-left Like -div but floats the div left.
359
+**
360
+** -div-right Like -div but floats the div right.
306361
**
307
-** -svg-src Stores the input pikchr's source code in the SVG's
308
-** metadata.
362
+** -svg-src Stores the input pikchr's source code in the output as
363
+** a separate element adjacent to the SVG one. The
364
+** source element initially has the "hidden" CSS class.
309365
**
310366
** -th Process the input using TH1 before passing it to pikchr.
311367
**
312368
** -th-novar Disable $var and $<var> TH1 processing. Use this if the
313369
** pikchr script uses '$' for its own purposes and that
@@ -340,20 +396,32 @@
340396
void pikchr_cmd(void){
341397
Blob bIn = empty_blob;
342398
Blob bOut = empty_blob;
343399
const char * zInfile = "-";
344400
const char * zOutfile = "-";
345
- const int fWithDiv = find_option("div",0,0)!=0;
346401
const int fTh1 = find_option("th",0,0)!=0;
347402
const int fNosvg = find_option("th-nosvg",0,0)!=0;
348403
int isErr = 0;
349404
int pikFlags = find_option("svg-src",0,0)!=0
350
- ? 0 : PIKCHR_PROCESS_NO_SRC;
405
+ ? PIKCHR_PROCESS_SRC_HIDDEN : 0;
351406
u32 fThFlags = TH_INIT_NO_ENCODE
352407
| (find_option("th-novar",0,0)!=0 ? TH_R2B_NO_VARS : 0);
353408
354409
Th_InitTraceLog()/*processes -th-trace flag*/;
410
+
411
+ if(find_option("div",0,0)!=0){
412
+ pikFlags |= PIKCHR_PROCESS_DIV;
413
+ }else if(find_option("div-indent",0,0)!=0){
414
+ pikFlags |= PIKCHR_PROCESS_DIV_INDENT;
415
+ }else if(find_option("div-center",0,0)!=0){
416
+ pikFlags |= PIKCHR_PROCESS_DIV_CENTER;
417
+ }else if(find_option("div-float-left",0,0)!=0){
418
+ pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_LEFT;
419
+ }else if(find_option("div-float-right",0,0)!=0){
420
+ pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_RIGHT;
421
+ }
422
+
355423
verify_all_options();
356424
if(g.argc>4){
357425
usage("?INFILE? ?OUTFILE?");
358426
}
359427
if(g.argc>2){
@@ -367,13 +435,10 @@
367435
db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0)
368436
/* ^^^ needed for certain TH1 functions to work */;;
369437
pikFlags |= PIKCHR_PROCESS_TH1;
370438
if(fNosvg) pikFlags |= PIKCHR_PROCESS_TH1_NOSVG;
371439
}
372
- if(fWithDiv){
373
- pikFlags |= PIKCHR_PROCESS_DIV;
374
- }
375440
isErr = pikchr_process(blob_str(&bIn), pikFlags,
376441
fTh1 ? fThFlags : 0, &bOut);
377442
if(isErr){
378443
/*fossil_print("ERROR: raw input:\n%b\n", &bIn);*/
379444
fossil_fatal("%s ERROR: %b", 1==isErr ? "TH1" : "pikchr",
380445
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -20,18 +20,23 @@
20 #include "config.h"
21 #include <assert.h>
22 #include <ctype.h>
23 #include "pikchrshow.h"
24
25 #ifdef INTERFACE
26 /* These are described in pikchr_process()'s docs. */
27 #define PIKCHR_PROCESS_TH1 0x01
28 #define PIKCHR_PROCESS_TH1_NOSVG 0x02
29 #define PIKCHR_PROCESS_DIV 0x04
30 #define PIKCHR_PROCESS_NONCE 0x08
31 #define PIKCHR_PROCESS_ERR_PRE 0x10
32 #define PIKCHR_PROCESS_NO_SRC 0x20
 
 
 
 
 
33 #endif
34
35 /*
36 ** Processes a pikchr script, optionally with embedded TH1. zIn is the
37 ** input script. pikFlags may be a bitmask of any of the
@@ -49,38 +54,62 @@
49 ** init flags specified in the 3rd argument. If thFlags is non-0 then
50 ** this flag is assumed even if it is not specified.
51 **
52 ** - PIKCHR_PROCESS_TH1_NOSVG means that processing stops after the
53 ** TH1 step, thus the output will be (presumably) a
54 ** TH1-generated/processed pikchr script, and not an SVG. If this flag
55 ** is set, PIKCHR_PROCESS_TH1 is assumed even if it is not specified.
 
56 **
57 ** The remaining flags listed below are ignored if
58 ** PIKCHR_PROCESS_TH1_NOSVG is specified:
59 **
60 ** - PIKCHR_PROCESS_NO_SRC: by default the contents of zIn are stored
61 ** in the resulting SVG content, as part of the image metadata. That
62 ** is suppressed if this flag is set.
63 **
64 ** - PIKCHR_PROCESS_DIV: if set, the SVG result is wrapped in a DIV
65 ** element which specifies a max-width style value based on the SVG's
66 ** calculated size.
 
 
 
 
 
 
 
 
67 **
68 ** - PIKCHR_PROCESS_NONCE: if set, the resulting SVG/DEV are wrapped
69 ** in "safe nonce" comments, which are a fossil-internal mechanism
70 ** which prevents the wiki/markdown processors from processing this
71 ** output.
 
 
 
 
 
 
 
 
72 **
73 ** - PIKCHR_PROCESS_ERR_PRE: if set and pikchr() fails, the resulting
74 ** error report is wrapped in PRE element.
 
75 */
76 int pikchr_process(const char * zIn, int pikFlags, int thFlags,
77 Blob * pOut){
78 Blob bIn = empty_blob;
79 int isErr = 0;
80
 
 
 
 
 
 
 
 
 
81 if(!(PIKCHR_PROCESS_TH1 & pikFlags)
 
82 && (PIKCHR_PROCESS_TH1_NOSVG & pikFlags || thFlags!=0)){
83 pikFlags |= PIKCHR_PROCESS_TH1;
84 }
85 if(PIKCHR_PROCESS_TH1 & pikFlags){
86 Blob out = empty_blob;
@@ -100,26 +129,45 @@
100 blob_append(pOut, blob_str(&bIn), blob_size(&bIn));
101 }else{
102 int w = 0, h = 0;
103 const char * zContent = blob_str(&bIn);
104 char *zOut;
105 const unsigned int pikFlags2 = (PIKCHR_PROCESS_NO_SRC & pikFlags)
106 ? 0 : PIKCHR_INCLUDE_SOURCE;
107
108 zOut = pikchr(zContent, "pikchr", pikFlags2, &w, &h);
109 if( w>0 && h>0 ){
110 const char *zNonce = (PIKCHR_PROCESS_NONCE & pikFlags)
111 ? safe_html_nonce(1) : 0;
112 if(zNonce){
113 blob_append(pOut, zNonce, -1);
114 }
115 if(PIKCHR_PROCESS_DIV & pikFlags){
116 blob_appendf(pOut,"<div style='max-width:%dpx;'>\n", w);
 
 
 
 
 
 
 
 
 
 
 
 
117 }
118 blob_append(pOut, zOut, -1);
 
 
 
 
 
 
 
 
 
119 if(PIKCHR_PROCESS_DIV & pikFlags){
120 blob_append(pOut,"</div>\n", 7);
121 }
122 if(zNonce){
123 blob_append(pOut, zNonce, -1);
124 }
125 }else{
@@ -150,10 +198,13 @@
150 ** value to pre-populate the editor with that code.
151 */
152 void pikchrshow_page(void){
153 const char *zContent = 0;
154 int isDark; /* true if the current skin is "dark" */
 
 
 
155
156 login_check_credentials();
157 if( !g.perm.RdWiki && !g.perm.Read && !g.perm.RdForum ){
158 cgi_redirectf("%s/login?g=%s/pikchrshow", g.zTop, g.zTop);
159 }
@@ -162,13 +213,11 @@
162 /* Called from the JS-side preview updater. */
163 cgi_set_content_type("text/html");
164 if(zContent && *zContent){
165 Blob out = empty_blob;
166 const int isErr =
167 pikchr_process(zContent,
168 PIKCHR_PROCESS_DIV | PIKCHR_PROCESS_ERR_PRE,
169 0, &out);
170 if(isErr){
171 cgi_printf_header("x-pikchrshow-is-error: %d\r\n", isErr);
172 }
173 CX("%b", &out);
174 blob_reset(&out);
@@ -268,12 +317,11 @@
268 /* Reminder: Firefox does not properly flexbox a LEGEND
269 element, always flowing it in column mode. */);
270 CX("<div id='pikchrshow-output'>");
271 if(*zContent){
272 Blob out = empty_blob;
273 pikchr_process(zContent, PIKCHR_PROCESS_ERR_PRE
274 | PIKCHR_PROCESS_DIV, 0, &out);
275 CX("%b", &out);
276 blob_reset(&out);
277 } CX("</div>"/*#pikchrshow-output*/);
278 } CX("</fieldset>"/*#pikchrshow-output-wrapper*/);
279 } CX("</div>"/*sbs-wrapper*/);
@@ -299,15 +347,23 @@
299 **
300 ** Options:
301 **
302 ** -div On success, adds a DIV wrapper around the
303 ** resulting SVG output which limits its max-width to
304 ** its computed maximum ideal size, in order to mimic
305 ** how fossil's web-based components work.
 
 
 
 
 
 
 
306 **
307 ** -svg-src Stores the input pikchr's source code in the SVG's
308 ** metadata.
 
309 **
310 ** -th Process the input using TH1 before passing it to pikchr.
311 **
312 ** -th-novar Disable $var and $<var> TH1 processing. Use this if the
313 ** pikchr script uses '$' for its own purposes and that
@@ -340,20 +396,32 @@
340 void pikchr_cmd(void){
341 Blob bIn = empty_blob;
342 Blob bOut = empty_blob;
343 const char * zInfile = "-";
344 const char * zOutfile = "-";
345 const int fWithDiv = find_option("div",0,0)!=0;
346 const int fTh1 = find_option("th",0,0)!=0;
347 const int fNosvg = find_option("th-nosvg",0,0)!=0;
348 int isErr = 0;
349 int pikFlags = find_option("svg-src",0,0)!=0
350 ? 0 : PIKCHR_PROCESS_NO_SRC;
351 u32 fThFlags = TH_INIT_NO_ENCODE
352 | (find_option("th-novar",0,0)!=0 ? TH_R2B_NO_VARS : 0);
353
354 Th_InitTraceLog()/*processes -th-trace flag*/;
 
 
 
 
 
 
 
 
 
 
 
 
 
355 verify_all_options();
356 if(g.argc>4){
357 usage("?INFILE? ?OUTFILE?");
358 }
359 if(g.argc>2){
@@ -367,13 +435,10 @@
367 db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0)
368 /* ^^^ needed for certain TH1 functions to work */;;
369 pikFlags |= PIKCHR_PROCESS_TH1;
370 if(fNosvg) pikFlags |= PIKCHR_PROCESS_TH1_NOSVG;
371 }
372 if(fWithDiv){
373 pikFlags |= PIKCHR_PROCESS_DIV;
374 }
375 isErr = pikchr_process(blob_str(&bIn), pikFlags,
376 fTh1 ? fThFlags : 0, &bOut);
377 if(isErr){
378 /*fossil_print("ERROR: raw input:\n%b\n", &bIn);*/
379 fossil_fatal("%s ERROR: %b", 1==isErr ? "TH1" : "pikchr",
380
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -20,18 +20,23 @@
20 #include "config.h"
21 #include <assert.h>
22 #include <ctype.h>
23 #include "pikchrshow.h"
24
25 #if INTERFACE
26 /* These are described in pikchr_process()'s docs. */
27 #define PIKCHR_PROCESS_TH1 0x01
28 #define PIKCHR_PROCESS_TH1_NOSVG 0x02
29 #define PIKCHR_PROCESS_NONCE 0x04
30 #define PIKCHR_PROCESS_ERR_PRE 0x08
31 #define PIKCHR_PROCESS_SRC 0x10
32 #define PIKCHR_PROCESS_SRC_HIDDEN 0x20
33 #define PIKCHR_PROCESS_DIV 0x40
34 #define PIKCHR_PROCESS_DIV_INDENT 0x0100
35 #define PIKCHR_PROCESS_DIV_CENTER 0x0200
36 #define PIKCHR_PROCESS_DIV_FLOAT_LEFT 0x0400
37 #define PIKCHR_PROCESS_DIV_FLOAT_RIGHT 0x0800
38 #endif
39
40 /*
41 ** Processes a pikchr script, optionally with embedded TH1. zIn is the
42 ** input script. pikFlags may be a bitmask of any of the
@@ -49,38 +54,62 @@
54 ** init flags specified in the 3rd argument. If thFlags is non-0 then
55 ** this flag is assumed even if it is not specified.
56 **
57 ** - PIKCHR_PROCESS_TH1_NOSVG means that processing stops after the
58 ** TH1 step, thus the output will be (presumably) a
59 ** TH1-generated/processed pikchr script (or whatever else the TH1
60 ** outputs). If this flag is set, PIKCHR_PROCESS_TH1 is assumed even
61 ** if it is not specified.
62 **
63 ** The remaining flags listed below are ignored if
64 ** PIKCHR_PROCESS_TH1_NOSVG is specified:
65 **
 
 
 
 
66 ** - PIKCHR_PROCESS_DIV: if set, the SVG result is wrapped in a DIV
67 ** element which specifies a max-width style value based on the SVG's
68 ** calculated size. This flag has multiple mutually exclusive forms:
69 **
70 ** - PIKCHR_PROCESS_DIV uses default element alignment.
71 ** - PIKCHR_PROCESS_DIV_INDENT indents the div.
72 ** - PIKCHR_PROCESS_DIV_CENTER centers the div.
73 ** - PIKCHR_PROCESS_DIV_FLOAT_LEFT floats the div left.
74 ** - PIKCHR_PROCESS_DIV_FLOAT_RIGHT floats the div right.
75 **
76 ** If more than one is specified, which one is used is undefined.
77 **
78 ** - PIKCHR_PROCESS_NONCE: if set, the resulting SVG/DIV are wrapped
79 ** in "safe nonce" comments, which are a fossil-internal mechanism
80 ** which prevents the wiki/markdown processors from re-processing this
81 ** output.
82 **
83 ** - PIKCHR_PROCESS_SRC: if set, a new TEXTAREA.pikchr-src element is injected
84 ** adjacet to the SVG element which contains the HTML-escaped content of
85 ** the input script.
86 **
87 ** - PIKCHR_PROCESS_SRC_HIDDEN: exactly like PIKCHR_PROCESS_SRC but
88 ** the .pikchr-src tag also gets the CSS class 'hidden' (which, in
89 ** fossil's default CSS, will hide that element).
90 **
91 ** - PIKCHR_PROCESS_ERR_PRE: if set and pikchr() fails, the resulting
92 ** error report is wrapped in a PRE element, else it is retained
93 ** as-is (intended for console output).
94 */
95 int pikchr_process(const char * zIn, int pikFlags, int thFlags,
96 Blob * pOut){
97 Blob bIn = empty_blob;
98 int isErr = 0;
99
100 if(!(PIKCHR_PROCESS_DIV & pikFlags)
101 /* If any DIV_xxx flags are set, set DIV */
102 && (PIKCHR_PROCESS_DIV_INDENT
103 | PIKCHR_PROCESS_DIV_CENTER
104 | PIKCHR_PROCESS_DIV_FLOAT_RIGHT
105 | PIKCHR_PROCESS_DIV_FLOAT_LEFT
106 ) & pikFlags){
107 pikFlags |= PIKCHR_PROCESS_DIV;
108 }
109 if(!(PIKCHR_PROCESS_TH1 & pikFlags)
110 /* If any TH1_xxx flags are set, set TH1 */
111 && (PIKCHR_PROCESS_TH1_NOSVG & pikFlags || thFlags!=0)){
112 pikFlags |= PIKCHR_PROCESS_TH1;
113 }
114 if(PIKCHR_PROCESS_TH1 & pikFlags){
115 Blob out = empty_blob;
@@ -100,26 +129,45 @@
129 blob_append(pOut, blob_str(&bIn), blob_size(&bIn));
130 }else{
131 int w = 0, h = 0;
132 const char * zContent = blob_str(&bIn);
133 char *zOut;
134
135 zOut = pikchr(zContent, "pikchr", 0, &w, &h);
 
 
136 if( w>0 && h>0 ){
137 const char *zNonce = (PIKCHR_PROCESS_NONCE & pikFlags)
138 ? safe_html_nonce(1) : 0;
139 if(zNonce){
140 blob_append(pOut, zNonce, -1);
141 }
142 if(PIKCHR_PROCESS_DIV & pikFlags){
143 Blob css = empty_blob;
144 blob_appendf(&css, "max-width:%dpx;", w);
145 if(PIKCHR_PROCESS_DIV_CENTER & pikFlags){
146 blob_append(&css, "display:block;margin-auto;", -1);
147 }else if(PIKCHR_PROCESS_DIV_INDENT & pikFlags){
148 blob_append(&css, "margin-left:4em", -1);
149 }else if(PIKCHR_PROCESS_DIV_FLOAT_LEFT & pikFlags){
150 blob_append(&css, "float:left;padding=4em;", -1);
151 }else if(PIKCHR_PROCESS_DIV_FLOAT_RIGHT & pikFlags){
152 blob_append(&css, "float:right;padding=4em;", -1);
153 }
154 blob_appendf(pOut,"<div class=\"pikchr\" style=\"%b\">\n", &css);
155 blob_reset(&css);
156 }
157 blob_append(pOut, zOut, -1);
158 if((PIKCHR_PROCESS_SRC & pikFlags)
159 || (PIKCHR_PROCESS_SRC_HIDDEN & pikFlags)){
160 blob_appendf(pOut, "<textarea rows='10' readonly "
161 "class='pikchr-src%s'>"
162 "%h</textarea>\n",
163 (PIKCHR_PROCESS_SRC_HIDDEN & pikFlags)
164 ? " hidden" : "",
165 blob_str(&bIn));
166 }
167 if(PIKCHR_PROCESS_DIV & pikFlags){
168 blob_append(pOut, "</div>\n", 7);
169 }
170 if(zNonce){
171 blob_append(pOut, zNonce, -1);
172 }
173 }else{
@@ -150,10 +198,13 @@
198 ** value to pre-populate the editor with that code.
199 */
200 void pikchrshow_page(void){
201 const char *zContent = 0;
202 int isDark; /* true if the current skin is "dark" */
203 int pikFlags = PIKCHR_PROCESS_DIV
204 | PIKCHR_PROCESS_SRC_HIDDEN
205 | PIKCHR_PROCESS_ERR_PRE;
206
207 login_check_credentials();
208 if( !g.perm.RdWiki && !g.perm.Read && !g.perm.RdForum ){
209 cgi_redirectf("%s/login?g=%s/pikchrshow", g.zTop, g.zTop);
210 }
@@ -162,13 +213,11 @@
213 /* Called from the JS-side preview updater. */
214 cgi_set_content_type("text/html");
215 if(zContent && *zContent){
216 Blob out = empty_blob;
217 const int isErr =
218 pikchr_process(zContent, pikFlags, 0, &out);
 
 
219 if(isErr){
220 cgi_printf_header("x-pikchrshow-is-error: %d\r\n", isErr);
221 }
222 CX("%b", &out);
223 blob_reset(&out);
@@ -268,12 +317,11 @@
317 /* Reminder: Firefox does not properly flexbox a LEGEND
318 element, always flowing it in column mode. */);
319 CX("<div id='pikchrshow-output'>");
320 if(*zContent){
321 Blob out = empty_blob;
322 pikchr_process(zContent, pikFlags, 0, &out);
 
323 CX("%b", &out);
324 blob_reset(&out);
325 } CX("</div>"/*#pikchrshow-output*/);
326 } CX("</fieldset>"/*#pikchrshow-output-wrapper*/);
327 } CX("</div>"/*sbs-wrapper*/);
@@ -299,15 +347,23 @@
347 **
348 ** Options:
349 **
350 ** -div On success, adds a DIV wrapper around the
351 ** resulting SVG output which limits its max-width to
352 ** its computed maximum ideal size.
353 **
354 ** -div-indent Like -div but indents the div.
355 **
356 ** -div-center Like -div but centers the div.
357 **
358 ** -div-left Like -div but floats the div left.
359 **
360 ** -div-right Like -div but floats the div right.
361 **
362 ** -svg-src Stores the input pikchr's source code in the output as
363 ** a separate element adjacent to the SVG one. The
364 ** source element initially has the "hidden" CSS class.
365 **
366 ** -th Process the input using TH1 before passing it to pikchr.
367 **
368 ** -th-novar Disable $var and $<var> TH1 processing. Use this if the
369 ** pikchr script uses '$' for its own purposes and that
@@ -340,20 +396,32 @@
396 void pikchr_cmd(void){
397 Blob bIn = empty_blob;
398 Blob bOut = empty_blob;
399 const char * zInfile = "-";
400 const char * zOutfile = "-";
 
401 const int fTh1 = find_option("th",0,0)!=0;
402 const int fNosvg = find_option("th-nosvg",0,0)!=0;
403 int isErr = 0;
404 int pikFlags = find_option("svg-src",0,0)!=0
405 ? PIKCHR_PROCESS_SRC_HIDDEN : 0;
406 u32 fThFlags = TH_INIT_NO_ENCODE
407 | (find_option("th-novar",0,0)!=0 ? TH_R2B_NO_VARS : 0);
408
409 Th_InitTraceLog()/*processes -th-trace flag*/;
410
411 if(find_option("div",0,0)!=0){
412 pikFlags |= PIKCHR_PROCESS_DIV;
413 }else if(find_option("div-indent",0,0)!=0){
414 pikFlags |= PIKCHR_PROCESS_DIV_INDENT;
415 }else if(find_option("div-center",0,0)!=0){
416 pikFlags |= PIKCHR_PROCESS_DIV_CENTER;
417 }else if(find_option("div-float-left",0,0)!=0){
418 pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_LEFT;
419 }else if(find_option("div-float-right",0,0)!=0){
420 pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_RIGHT;
421 }
422
423 verify_all_options();
424 if(g.argc>4){
425 usage("?INFILE? ?OUTFILE?");
426 }
427 if(g.argc>2){
@@ -367,13 +435,10 @@
435 db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0)
436 /* ^^^ needed for certain TH1 functions to work */;;
437 pikFlags |= PIKCHR_PROCESS_TH1;
438 if(fNosvg) pikFlags |= PIKCHR_PROCESS_TH1_NOSVG;
439 }
 
 
 
440 isErr = pikchr_process(blob_str(&bIn), pikFlags,
441 fTh1 ? fThFlags : 0, &bOut);
442 if(isErr){
443 /*fossil_print("ERROR: raw input:\n%b\n", &bIn);*/
444 fossil_fatal("%s ERROR: %b", 1==isErr ? "TH1" : "pikchr",
445

Keyboard Shortcuts

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