Fossil SCM

Merge in trunk (check-in [9543ddbef2])

george 2020-08-26 16:11 wiki-history merge
Commit b56460fc961d67954369bc64c3ad7ccdd1466dd9b893b9be154861c92aabecf8
+3 -3
--- src/add.c
+++ src/add.c
@@ -551,11 +551,11 @@
551551
**
552552
** Options:
553553
** --soft Skip removing files from the checkout.
554554
** This supersedes the --hard option.
555555
** --hard Remove files from the checkout.
556
-** --case-sensitive <BOOL> Override the case-sensitive setting.
556
+** --case-sensitive BOOL Override the case-sensitive setting.
557557
** -n|--dry-run If given, display instead of run actions.
558558
** --reset Reset the DELETED state of a checkout, such
559559
** that all newly-rm'd (but not yet committed)
560560
** files are no longer removed. No flags other
561561
** than --verbose or --dry-run may be used with
@@ -653,11 +653,11 @@
653653
** the case-sensitive setting is undefined, then case sensitivity
654654
** defaults off for Cygwin, Mac and Windows and on for all other unix.
655655
** If case-sensitivity is enabled in the windows kernel, the Cygwin port
656656
** of fossil.exe can detect that, and modifies the default to 'on'.
657657
**
658
-** The --case-sensitive <BOOL> command-line option overrides any
658
+** The "--case-sensitive BOOL" command-line option overrides any
659659
** setting.
660660
*/
661661
int filenames_are_case_sensitive(void){
662662
static int caseSensitive;
663663
static int once = 1;
@@ -989,11 +989,11 @@
989989
**
990990
** Options:
991991
** --soft Skip moving files within the checkout.
992992
** This supersedes the --hard option.
993993
** --hard Move files within the checkout.
994
-** --case-sensitive <BOOL> Override the case-sensitive setting.
994
+** --case-sensitive BOOL Override the case-sensitive setting.
995995
** -n|--dry-run If given, display instead of run actions.
996996
**
997997
** See also: [[changes]], [[status]]
998998
*/
999999
void mv_cmd(void){
10001000
--- src/add.c
+++ src/add.c
@@ -551,11 +551,11 @@
551 **
552 ** Options:
553 ** --soft Skip removing files from the checkout.
554 ** This supersedes the --hard option.
555 ** --hard Remove files from the checkout.
556 ** --case-sensitive <BOOL> Override the case-sensitive setting.
557 ** -n|--dry-run If given, display instead of run actions.
558 ** --reset Reset the DELETED state of a checkout, such
559 ** that all newly-rm'd (but not yet committed)
560 ** files are no longer removed. No flags other
561 ** than --verbose or --dry-run may be used with
@@ -653,11 +653,11 @@
653 ** the case-sensitive setting is undefined, then case sensitivity
654 ** defaults off for Cygwin, Mac and Windows and on for all other unix.
655 ** If case-sensitivity is enabled in the windows kernel, the Cygwin port
656 ** of fossil.exe can detect that, and modifies the default to 'on'.
657 **
658 ** The --case-sensitive <BOOL> command-line option overrides any
659 ** setting.
660 */
661 int filenames_are_case_sensitive(void){
662 static int caseSensitive;
663 static int once = 1;
@@ -989,11 +989,11 @@
989 **
990 ** Options:
991 ** --soft Skip moving files within the checkout.
992 ** This supersedes the --hard option.
993 ** --hard Move files within the checkout.
994 ** --case-sensitive <BOOL> Override the case-sensitive setting.
995 ** -n|--dry-run If given, display instead of run actions.
996 **
997 ** See also: [[changes]], [[status]]
998 */
999 void mv_cmd(void){
1000
--- src/add.c
+++ src/add.c
@@ -551,11 +551,11 @@
551 **
552 ** Options:
553 ** --soft Skip removing files from the checkout.
554 ** This supersedes the --hard option.
555 ** --hard Remove files from the checkout.
556 ** --case-sensitive BOOL Override the case-sensitive setting.
557 ** -n|--dry-run If given, display instead of run actions.
558 ** --reset Reset the DELETED state of a checkout, such
559 ** that all newly-rm'd (but not yet committed)
560 ** files are no longer removed. No flags other
561 ** than --verbose or --dry-run may be used with
@@ -653,11 +653,11 @@
653 ** the case-sensitive setting is undefined, then case sensitivity
654 ** defaults off for Cygwin, Mac and Windows and on for all other unix.
655 ** If case-sensitivity is enabled in the windows kernel, the Cygwin port
656 ** of fossil.exe can detect that, and modifies the default to 'on'.
657 **
658 ** The "--case-sensitive BOOL" command-line option overrides any
659 ** setting.
660 */
661 int filenames_are_case_sensitive(void){
662 static int caseSensitive;
663 static int once = 1;
@@ -989,11 +989,11 @@
989 **
990 ** Options:
991 ** --soft Skip moving files within the checkout.
992 ** This supersedes the --hard option.
993 ** --hard Move files within the checkout.
994 ** --case-sensitive BOOL Override the case-sensitive setting.
995 ** -n|--dry-run If given, display instead of run actions.
996 **
997 ** See also: [[changes]], [[status]]
998 */
999 void mv_cmd(void){
1000
+2 -2
--- src/ajax.c
+++ src/ajax.c
@@ -41,11 +41,11 @@
4141
** and symbolic names for use by client-side scripts.
4242
**
4343
** If addScriptTag is true then the output is wrapped in a SCRIPT tag
4444
** with the current nonce, else no SCRIPT tag is emitted.
4545
**
46
-** Requires that style_emit_script_fossil_bootstrap() has already been
46
+** Requires that builtin_emit_script_fossil_bootstrap() has already been
4747
** called in order to initialize the window.fossil.page object.
4848
*/
4949
void ajax_emit_js_preview_modes(int addScriptTag){
5050
if(addScriptTag){
5151
style_emit_script_tag(0,0);
@@ -131,11 +131,11 @@
131131
break;
132132
default:{
133133
const char *zContent = blob_str(pContent);
134134
if(AJAX_PREVIEW_LINE_NUMBERS & flags){
135135
output_text_with_line_numbers(zContent, blob_size(pContent),
136
- zName, "on");
136
+ zName, "on", 0);
137137
}else{
138138
const char *zExt = strrchr(zName,'.');
139139
if(zExt && zExt[1]){
140140
CX("<pre><code class='language-%s'>%h</code></pre>",
141141
zExt+1, zContent);
142142
--- src/ajax.c
+++ src/ajax.c
@@ -41,11 +41,11 @@
41 ** and symbolic names for use by client-side scripts.
42 **
43 ** If addScriptTag is true then the output is wrapped in a SCRIPT tag
44 ** with the current nonce, else no SCRIPT tag is emitted.
45 **
46 ** Requires that style_emit_script_fossil_bootstrap() has already been
47 ** called in order to initialize the window.fossil.page object.
48 */
49 void ajax_emit_js_preview_modes(int addScriptTag){
50 if(addScriptTag){
51 style_emit_script_tag(0,0);
@@ -131,11 +131,11 @@
131 break;
132 default:{
133 const char *zContent = blob_str(pContent);
134 if(AJAX_PREVIEW_LINE_NUMBERS & flags){
135 output_text_with_line_numbers(zContent, blob_size(pContent),
136 zName, "on");
137 }else{
138 const char *zExt = strrchr(zName,'.');
139 if(zExt && zExt[1]){
140 CX("<pre><code class='language-%s'>%h</code></pre>",
141 zExt+1, zContent);
142
--- src/ajax.c
+++ src/ajax.c
@@ -41,11 +41,11 @@
41 ** and symbolic names for use by client-side scripts.
42 **
43 ** If addScriptTag is true then the output is wrapped in a SCRIPT tag
44 ** with the current nonce, else no SCRIPT tag is emitted.
45 **
46 ** Requires that builtin_emit_script_fossil_bootstrap() has already been
47 ** called in order to initialize the window.fossil.page object.
48 */
49 void ajax_emit_js_preview_modes(int addScriptTag){
50 if(addScriptTag){
51 style_emit_script_tag(0,0);
@@ -131,11 +131,11 @@
131 break;
132 default:{
133 const char *zContent = blob_str(pContent);
134 if(AJAX_PREVIEW_LINE_NUMBERS & flags){
135 output_text_with_line_numbers(zContent, blob_size(pContent),
136 zName, "on", 0);
137 }else{
138 const char *zExt = strrchr(zName,'.');
139 if(zExt && zExt[1]){
140 CX("<pre><code class='language-%s'>%h</code></pre>",
141 zExt+1, zContent);
142
+1 -1
--- src/attach.c
+++ src/attach.c
@@ -617,11 +617,11 @@
617617
const char *z;
618618
content_get(ridSrc, &attach);
619619
blob_to_utf8_no_bom(&attach, 0);
620620
z = blob_str(&attach);
621621
if( zLn ){
622
- output_text_with_line_numbers(z, blob_size(&attach), zName, zLn);
622
+ output_text_with_line_numbers(z, blob_size(&attach), zName, zLn, 1);
623623
}else{
624624
@ <pre>
625625
@ %h(z)
626626
@ </pre>
627627
}
628628
--- src/attach.c
+++ src/attach.c
@@ -617,11 +617,11 @@
617 const char *z;
618 content_get(ridSrc, &attach);
619 blob_to_utf8_no_bom(&attach, 0);
620 z = blob_str(&attach);
621 if( zLn ){
622 output_text_with_line_numbers(z, blob_size(&attach), zName, zLn);
623 }else{
624 @ <pre>
625 @ %h(z)
626 @ </pre>
627 }
628
--- src/attach.c
+++ src/attach.c
@@ -617,11 +617,11 @@
617 const char *z;
618 content_get(ridSrc, &attach);
619 blob_to_utf8_no_bom(&attach, 0);
620 z = blob_str(&attach);
621 if( zLn ){
622 output_text_with_line_numbers(z, blob_size(&attach), zName, zLn, 1);
623 }else{
624 @ <pre>
625 @ %h(z)
626 @ </pre>
627 }
628
+190
--- src/builtin.c
+++ src/builtin.c
@@ -258,10 +258,18 @@
258258
}else if( !bSilent ){
259259
fossil_fatal("unknown javascript delivery mode \"%s\" - should be"
260260
" one of: inline separate bundled", zMode);
261261
}
262262
}
263
+
264
+/*
265
+** Returns the current JS delivery mode: one of JS_INLINE,
266
+** JS_SEPARATE, JS_BUNDLED.
267
+*/
268
+int builtin_get_js_delivery_mode(void){
269
+ return builtin.eDelivery;
270
+}
263271
264272
/*
265273
** The caller wants the Javascript file named by zFilename to be
266274
** included in the generated page. Add the file to the queue of
267275
** requested javascript resources, if it is not there already.
@@ -559,5 +567,187 @@
559567
int rc = sqlite3_create_module(db, "builtin", &builtinVtabModule, 0);
560568
return rc;
561569
}
562570
/* End of the builtin virtual table
563571
******************************************************************************/
572
+
573
+
574
+/*
575
+** The first time this is called, it emits code to install and
576
+** bootstrap the window.fossil object, using the built-in file
577
+** fossil.bootstrap.js (not to be confused with bootstrap.js).
578
+**
579
+** Subsequent calls are no-ops.
580
+**
581
+** It emits 2 parts:
582
+**
583
+** 1) window.fossil core object, some of which depends on C-level
584
+** runtime data. That part of the script is always emitted inline. If
585
+** addScriptTag is true then it is wrapped in its own SCRIPT tag, else
586
+** it is assumed that the caller already opened a tag.
587
+**
588
+** 2) Emits the static fossil.bootstrap.js using builtin_request_js().
589
+*/
590
+void builtin_emit_script_fossil_bootstrap(int addScriptTag){
591
+ static int once = 0;
592
+ if(0==once++){
593
+ char * zName;
594
+ /* Set up the generic/app-agnostic parts of window.fossil
595
+ ** which require C-level state... */
596
+ if(addScriptTag!=0){
597
+ style_emit_script_tag(0,0);
598
+ }
599
+ CX("(function(){\n");
600
+ CX(/*MSIE NodeList.forEach polyfill, courtesy of Mozilla:
601
+ https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach#Polyfill
602
+ */
603
+ "if(window.NodeList && !NodeList.prototype.forEach){"
604
+ "NodeList.prototype.forEach = Array.prototype.forEach;"
605
+ "}\n");
606
+ CX("if(!window.fossil) window.fossil={};\n"
607
+ "window.fossil.version = %!j;\n"
608
+ /* fossil.rootPath is the top-most CGI/server path,
609
+ ** including a trailing slash. */
610
+ "window.fossil.rootPath = %!j+'/';\n",
611
+ get_version(), g.zTop);
612
+ /* fossil.config = {...various config-level options...} */
613
+ CX("window.fossil.config = {");
614
+ zName = db_get("project-name", "");
615
+ CX("projectName: %!j,\n", zName);
616
+ fossil_free(zName);
617
+ zName = db_get("short-project-name", "");
618
+ CX("shortProjectName: %!j,\n", zName);
619
+ fossil_free(zName);
620
+ zName = db_get("project-code", "");
621
+ CX("projectCode: %!j,\n", zName);
622
+ fossil_free(zName);
623
+ CX("/* Length of UUID hashes for display purposes. */");
624
+ CX("hashDigits: %d, hashDigitsUrl: %d,\n",
625
+ hash_digits(0), hash_digits(1));
626
+ CX("editStateMarkers: {"
627
+ "/*Symbolic markers to denote certain edit states.*/"
628
+ "isNew:'[+]', isModified:'[*]', isDeleted:'[-]'},\n");
629
+ CX("confirmerButtonTicks: 3 "
630
+ "/*default fossil.confirmer tick count.*/\n");
631
+ CX("};\n"/* fossil.config */);
632
+#if 0
633
+ /* Is it safe to emit the CSRF token here? Some pages add it
634
+ ** as a hidden form field. */
635
+ if(g.zCsrfToken[0]!=0){
636
+ CX("window.fossil.csrfToken = %!j;\n",
637
+ g.zCsrfToken);
638
+ }
639
+#endif
640
+ /*
641
+ ** fossil.page holds info about the current page. This is also
642
+ ** where the current page "should" store any of its own
643
+ ** page-specific state, and it is reserved for that purpose.
644
+ */
645
+ CX("window.fossil.page = {"
646
+ "name:\"%T\""
647
+ "};\n", g.zPath);
648
+ CX("})();\n");
649
+ if(addScriptTag!=0){
650
+ style_emit_script_tag(1,0);
651
+ }
652
+ /* The remaining window.fossil bootstrap code is not dependent on
653
+ ** C-runtime state... */
654
+ builtin_request_js("fossil.bootstrap.js");
655
+ }
656
+}
657
+
658
+
659
+/*
660
+** Convenience wrapper which calls builtin_request_js() for a series
661
+** of builtin scripts named fossil.NAME.js. The first time it is
662
+** called, it also calls builtin_emit_script_fossil_bootstrap() to
663
+** initialize the window.fossil JS API. The first argument is the NAME
664
+** part of the first API to emit. All subsequent arguments must be
665
+** strings of the NAME part of additional fossil.NAME.js files,
666
+** followed by a NULL argument to terminate the list.
667
+**
668
+** e.g. pass it ("fetch", "dom", "tabs", 0) to load those 3
669
+** APIs. Do not forget the trailing 0!
670
+**
671
+** In practice it is normally necessary (or preferred) to call
672
+** builtin_fulfill_js_requests() after calling this, before proceeding
673
+** to call builtin_request_js() for page-specific JS, in order to
674
+** improve cachability.
675
+**
676
+** Achtung: the fossil.page.XYZ.js files are page-specific, containing
677
+** the app-level logic for that specific page, and loading more than
678
+** one of them in a single pagee will break that page. Each of those
679
+** expects to "own" the page it is loaded in, and it should be loaded
680
+** as late in the JS-loading process as feasible, ideally bundled (via
681
+** builtin_request_js()) with any other app-/page-specific JS it may
682
+** need.
683
+*/
684
+void builtin_emit_fossil_js_apis( const char * zApi, ... ) {
685
+ static int once = 0;
686
+ const char *zArg;
687
+ char * zName;
688
+ va_list vargs;
689
+
690
+ if(0==once++){
691
+ builtin_emit_script_fossil_bootstrap(1);
692
+ }
693
+ zName = mprintf("fossil.%s.js", zApi);
694
+ builtin_request_js(zName);
695
+ fossil_free(zName);
696
+
697
+ va_start(vargs,zApi);
698
+ while( (zArg = va_arg (vargs, const char *))!=0 ){
699
+ zName = mprintf("fossil.%s.js", zArg);
700
+ builtin_request_js(zName);
701
+ fossil_free(zName);
702
+ }
703
+ va_end(vargs);
704
+}
705
+
706
+/*
707
+** If builtin_get_js_delivery_mode() returns JS_BUNDLED then this
708
+** function emits, via builtin_request_js(), all JS fossil.XYZ APIs
709
+** which are not strictly specific to a single page, and then calls
710
+** builtin_fulfill_js_requests(). The idea is that we can get better
711
+** bundle caching and reduced HTTP requests by including all JS,
712
+** rather than creating separate bundles on a per-page basis. It then
713
+** returns true. As a special case, if this is called more than once
714
+** in bundled mode, subsequent calls are a no-op.
715
+**
716
+** If the current JS delivery mode is *not* JS_BUNDLED then this
717
+** function is a no-op and returns false. The reason is simply because
718
+** bundled mode is the only mode in which this API improves aggregate
719
+** over-the-wire and HTTP request costs. For other modes, reducing the
720
+** inclusion of fossil.XYZ APIs to their bare minimum, provides the
721
+** lowest aggregate costs. For debate and details, see the discussion
722
+** at:
723
+**
724
+** https://fossil-scm.org/forum/forumpost/3fa2633f3e
725
+**
726
+** Minor caveat: the purpose of emitting all of the fossil.XYZ JS APIs
727
+** at once is to reduce over-the-wire transfers by enabling cross-page
728
+** caching, but if there are other JS scripts pending via
729
+** builtin_request_js() when this is called then they will be included
730
+** in the JS request emitted by this routine, resulting in a different
731
+** script URL than if they were not included. Thus, if a given page
732
+** has its own scripts to install via builtin_request_js(), they
733
+** should, if possible, be delayed until after this is called OR the
734
+** page should call builtin_fulfill_js_requests() to flush the request
735
+** queue before calling this routine.
736
+*/
737
+int builtin_bundle_all_fossil_js_apis(void){
738
+ static int bundled = 0;
739
+ if(JS_BUNDLED == builtin_get_js_delivery_mode()){
740
+ if(!bundled){
741
+ bundled = 1;
742
+ builtin_emit_fossil_js_apis("dom", "fetch",
743
+ "storage", "tabs",
744
+ "confirmer", "popupwidget",
745
+ "copybutton", "numbered-lines",
746
+ 0);
747
+ builtin_fulfill_js_requests();
748
+ }
749
+ return 1;
750
+ }else{
751
+ return 0;
752
+ }
753
+}
564754
--- src/builtin.c
+++ src/builtin.c
@@ -258,10 +258,18 @@
258 }else if( !bSilent ){
259 fossil_fatal("unknown javascript delivery mode \"%s\" - should be"
260 " one of: inline separate bundled", zMode);
261 }
262 }
 
 
 
 
 
 
 
 
263
264 /*
265 ** The caller wants the Javascript file named by zFilename to be
266 ** included in the generated page. Add the file to the queue of
267 ** requested javascript resources, if it is not there already.
@@ -559,5 +567,187 @@
559 int rc = sqlite3_create_module(db, "builtin", &builtinVtabModule, 0);
560 return rc;
561 }
562 /* End of the builtin virtual table
563 ******************************************************************************/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
564
--- src/builtin.c
+++ src/builtin.c
@@ -258,10 +258,18 @@
258 }else if( !bSilent ){
259 fossil_fatal("unknown javascript delivery mode \"%s\" - should be"
260 " one of: inline separate bundled", zMode);
261 }
262 }
263
264 /*
265 ** Returns the current JS delivery mode: one of JS_INLINE,
266 ** JS_SEPARATE, JS_BUNDLED.
267 */
268 int builtin_get_js_delivery_mode(void){
269 return builtin.eDelivery;
270 }
271
272 /*
273 ** The caller wants the Javascript file named by zFilename to be
274 ** included in the generated page. Add the file to the queue of
275 ** requested javascript resources, if it is not there already.
@@ -559,5 +567,187 @@
567 int rc = sqlite3_create_module(db, "builtin", &builtinVtabModule, 0);
568 return rc;
569 }
570 /* End of the builtin virtual table
571 ******************************************************************************/
572
573
574 /*
575 ** The first time this is called, it emits code to install and
576 ** bootstrap the window.fossil object, using the built-in file
577 ** fossil.bootstrap.js (not to be confused with bootstrap.js).
578 **
579 ** Subsequent calls are no-ops.
580 **
581 ** It emits 2 parts:
582 **
583 ** 1) window.fossil core object, some of which depends on C-level
584 ** runtime data. That part of the script is always emitted inline. If
585 ** addScriptTag is true then it is wrapped in its own SCRIPT tag, else
586 ** it is assumed that the caller already opened a tag.
587 **
588 ** 2) Emits the static fossil.bootstrap.js using builtin_request_js().
589 */
590 void builtin_emit_script_fossil_bootstrap(int addScriptTag){
591 static int once = 0;
592 if(0==once++){
593 char * zName;
594 /* Set up the generic/app-agnostic parts of window.fossil
595 ** which require C-level state... */
596 if(addScriptTag!=0){
597 style_emit_script_tag(0,0);
598 }
599 CX("(function(){\n");
600 CX(/*MSIE NodeList.forEach polyfill, courtesy of Mozilla:
601 https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach#Polyfill
602 */
603 "if(window.NodeList && !NodeList.prototype.forEach){"
604 "NodeList.prototype.forEach = Array.prototype.forEach;"
605 "}\n");
606 CX("if(!window.fossil) window.fossil={};\n"
607 "window.fossil.version = %!j;\n"
608 /* fossil.rootPath is the top-most CGI/server path,
609 ** including a trailing slash. */
610 "window.fossil.rootPath = %!j+'/';\n",
611 get_version(), g.zTop);
612 /* fossil.config = {...various config-level options...} */
613 CX("window.fossil.config = {");
614 zName = db_get("project-name", "");
615 CX("projectName: %!j,\n", zName);
616 fossil_free(zName);
617 zName = db_get("short-project-name", "");
618 CX("shortProjectName: %!j,\n", zName);
619 fossil_free(zName);
620 zName = db_get("project-code", "");
621 CX("projectCode: %!j,\n", zName);
622 fossil_free(zName);
623 CX("/* Length of UUID hashes for display purposes. */");
624 CX("hashDigits: %d, hashDigitsUrl: %d,\n",
625 hash_digits(0), hash_digits(1));
626 CX("editStateMarkers: {"
627 "/*Symbolic markers to denote certain edit states.*/"
628 "isNew:'[+]', isModified:'[*]', isDeleted:'[-]'},\n");
629 CX("confirmerButtonTicks: 3 "
630 "/*default fossil.confirmer tick count.*/\n");
631 CX("};\n"/* fossil.config */);
632 #if 0
633 /* Is it safe to emit the CSRF token here? Some pages add it
634 ** as a hidden form field. */
635 if(g.zCsrfToken[0]!=0){
636 CX("window.fossil.csrfToken = %!j;\n",
637 g.zCsrfToken);
638 }
639 #endif
640 /*
641 ** fossil.page holds info about the current page. This is also
642 ** where the current page "should" store any of its own
643 ** page-specific state, and it is reserved for that purpose.
644 */
645 CX("window.fossil.page = {"
646 "name:\"%T\""
647 "};\n", g.zPath);
648 CX("})();\n");
649 if(addScriptTag!=0){
650 style_emit_script_tag(1,0);
651 }
652 /* The remaining window.fossil bootstrap code is not dependent on
653 ** C-runtime state... */
654 builtin_request_js("fossil.bootstrap.js");
655 }
656 }
657
658
659 /*
660 ** Convenience wrapper which calls builtin_request_js() for a series
661 ** of builtin scripts named fossil.NAME.js. The first time it is
662 ** called, it also calls builtin_emit_script_fossil_bootstrap() to
663 ** initialize the window.fossil JS API. The first argument is the NAME
664 ** part of the first API to emit. All subsequent arguments must be
665 ** strings of the NAME part of additional fossil.NAME.js files,
666 ** followed by a NULL argument to terminate the list.
667 **
668 ** e.g. pass it ("fetch", "dom", "tabs", 0) to load those 3
669 ** APIs. Do not forget the trailing 0!
670 **
671 ** In practice it is normally necessary (or preferred) to call
672 ** builtin_fulfill_js_requests() after calling this, before proceeding
673 ** to call builtin_request_js() for page-specific JS, in order to
674 ** improve cachability.
675 **
676 ** Achtung: the fossil.page.XYZ.js files are page-specific, containing
677 ** the app-level logic for that specific page, and loading more than
678 ** one of them in a single pagee will break that page. Each of those
679 ** expects to "own" the page it is loaded in, and it should be loaded
680 ** as late in the JS-loading process as feasible, ideally bundled (via
681 ** builtin_request_js()) with any other app-/page-specific JS it may
682 ** need.
683 */
684 void builtin_emit_fossil_js_apis( const char * zApi, ... ) {
685 static int once = 0;
686 const char *zArg;
687 char * zName;
688 va_list vargs;
689
690 if(0==once++){
691 builtin_emit_script_fossil_bootstrap(1);
692 }
693 zName = mprintf("fossil.%s.js", zApi);
694 builtin_request_js(zName);
695 fossil_free(zName);
696
697 va_start(vargs,zApi);
698 while( (zArg = va_arg (vargs, const char *))!=0 ){
699 zName = mprintf("fossil.%s.js", zArg);
700 builtin_request_js(zName);
701 fossil_free(zName);
702 }
703 va_end(vargs);
704 }
705
706 /*
707 ** If builtin_get_js_delivery_mode() returns JS_BUNDLED then this
708 ** function emits, via builtin_request_js(), all JS fossil.XYZ APIs
709 ** which are not strictly specific to a single page, and then calls
710 ** builtin_fulfill_js_requests(). The idea is that we can get better
711 ** bundle caching and reduced HTTP requests by including all JS,
712 ** rather than creating separate bundles on a per-page basis. It then
713 ** returns true. As a special case, if this is called more than once
714 ** in bundled mode, subsequent calls are a no-op.
715 **
716 ** If the current JS delivery mode is *not* JS_BUNDLED then this
717 ** function is a no-op and returns false. The reason is simply because
718 ** bundled mode is the only mode in which this API improves aggregate
719 ** over-the-wire and HTTP request costs. For other modes, reducing the
720 ** inclusion of fossil.XYZ APIs to their bare minimum, provides the
721 ** lowest aggregate costs. For debate and details, see the discussion
722 ** at:
723 **
724 ** https://fossil-scm.org/forum/forumpost/3fa2633f3e
725 **
726 ** Minor caveat: the purpose of emitting all of the fossil.XYZ JS APIs
727 ** at once is to reduce over-the-wire transfers by enabling cross-page
728 ** caching, but if there are other JS scripts pending via
729 ** builtin_request_js() when this is called then they will be included
730 ** in the JS request emitted by this routine, resulting in a different
731 ** script URL than if they were not included. Thus, if a given page
732 ** has its own scripts to install via builtin_request_js(), they
733 ** should, if possible, be delayed until after this is called OR the
734 ** page should call builtin_fulfill_js_requests() to flush the request
735 ** queue before calling this routine.
736 */
737 int builtin_bundle_all_fossil_js_apis(void){
738 static int bundled = 0;
739 if(JS_BUNDLED == builtin_get_js_delivery_mode()){
740 if(!bundled){
741 bundled = 1;
742 builtin_emit_fossil_js_apis("dom", "fetch",
743 "storage", "tabs",
744 "confirmer", "popupwidget",
745 "copybutton", "numbered-lines",
746 0);
747 builtin_fulfill_js_requests();
748 }
749 return 1;
750 }else{
751 return 0;
752 }
753 }
754
+1 -1
--- src/checkin.c
+++ src/checkin.c
@@ -416,11 +416,11 @@
416416
** --abs-paths Display absolute pathnames.
417417
** --rel-paths Display pathnames relative to the current working
418418
** directory.
419419
** --hash Verify file status using hashing rather than
420420
** relying on file mtimes.
421
-** --case-sensitive <BOOL> Override case-sensitive setting.
421
+** --case-sensitive BOOL Override case-sensitive setting.
422422
** --dotfiles Include unmanaged files beginning with a dot.
423423
** --ignore <CSG> Ignore unmanaged files matching CSG glob patterns.
424424
**
425425
** Options specific to the changes command:
426426
** --header Identify the repository if report is non-empty.
427427
--- src/checkin.c
+++ src/checkin.c
@@ -416,11 +416,11 @@
416 ** --abs-paths Display absolute pathnames.
417 ** --rel-paths Display pathnames relative to the current working
418 ** directory.
419 ** --hash Verify file status using hashing rather than
420 ** relying on file mtimes.
421 ** --case-sensitive <BOOL> Override case-sensitive setting.
422 ** --dotfiles Include unmanaged files beginning with a dot.
423 ** --ignore <CSG> Ignore unmanaged files matching CSG glob patterns.
424 **
425 ** Options specific to the changes command:
426 ** --header Identify the repository if report is non-empty.
427
--- src/checkin.c
+++ src/checkin.c
@@ -416,11 +416,11 @@
416 ** --abs-paths Display absolute pathnames.
417 ** --rel-paths Display pathnames relative to the current working
418 ** directory.
419 ** --hash Verify file status using hashing rather than
420 ** relying on file mtimes.
421 ** --case-sensitive BOOL Override case-sensitive setting.
422 ** --dotfiles Include unmanaged files beginning with a dot.
423 ** --ignore <CSG> Ignore unmanaged files matching CSG glob patterns.
424 **
425 ** Options specific to the changes command:
426 ** --header Identify the repository if report is non-empty.
427
+1 -1
--- src/comformat.c
+++ src/comformat.c
@@ -551,11 +551,11 @@
551551
** --wordbreak Attempt to break lines on word boundaries.
552552
** --origbreak Attempt to break when the original comment text
553553
** is detected.
554554
** --indent Number of spaces to indent (default (-1) is to
555555
** auto-detect). Zero means no indent.
556
-** -W|--width <num> Width of lines (default (-1) is to auto-detect).
556
+** -W|--width NUM Width of lines (default (-1) is to auto-detect).
557557
** Zero means no limit.
558558
*/
559559
void test_comment_format(void){
560560
const char *zWidth;
561561
const char *zIndent;
562562
--- src/comformat.c
+++ src/comformat.c
@@ -551,11 +551,11 @@
551 ** --wordbreak Attempt to break lines on word boundaries.
552 ** --origbreak Attempt to break when the original comment text
553 ** is detected.
554 ** --indent Number of spaces to indent (default (-1) is to
555 ** auto-detect). Zero means no indent.
556 ** -W|--width <num> Width of lines (default (-1) is to auto-detect).
557 ** Zero means no limit.
558 */
559 void test_comment_format(void){
560 const char *zWidth;
561 const char *zIndent;
562
--- src/comformat.c
+++ src/comformat.c
@@ -551,11 +551,11 @@
551 ** --wordbreak Attempt to break lines on word boundaries.
552 ** --origbreak Attempt to break when the original comment text
553 ** is detected.
554 ** --indent Number of spaces to indent (default (-1) is to
555 ** auto-detect). Zero means no indent.
556 ** -W|--width NUM Width of lines (default (-1) is to auto-detect).
557 ** Zero means no limit.
558 */
559 void test_comment_format(void){
560 const char *zWidth;
561 const char *zIndent;
562
+63 -6
--- src/default.css
+++ src/default.css
@@ -1100,15 +1100,16 @@
11001100
.font-size-200 {
11011101
font-size: 200%;
11021102
}
11031103
11041104
/**
1105
- .input-with-label is intended to be a wrapper element which
1106
- contain both a LABEL tag and an INPUT or SELECT control.
1107
- The wrapper is "necessary", as opposed to placing the INPUT
1108
- in the LABEL, so that we can include multiple INPUT
1109
- elements (e.g. a set of radio buttons).
1105
+ .input-with-label is intended to be a wrapper element which contain
1106
+ both a LABEL tag and an INPUT or SELECT control. The wrapper is
1107
+ "necessary", as opposed to placing the INPUT in the LABEL, so that
1108
+ we can include multiple INPUT elements (e.g. a set of radio
1109
+ buttons). Note that these elements must sometimes be BLOCK elements
1110
+ (e.g. DIV) so that certain nesting constructs are legal.
11101111
*/
11111112
.input-with-label {
11121113
border: 1px inset #808080;
11131114
border-radius: 0.25em;
11141115
padding: 0.25em 0.4em;
@@ -1251,11 +1252,11 @@
12511252
border: 1px solid black;
12521253
border-radius: 0.25em;
12531254
position: absolute;
12541255
display: inline-block;
12551256
z-index: 19/*below default skin's hamburger popup*/;
1256
- box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.75);
1257
+ box-shadow: -0.15em 0.15em 0.2em rgba(0, 0, 0, 0.75);
12571258
background-color: inherit;
12581259
}
12591260
12601261
.fossil-toast-message {
12611262
/* "toast"-style popup message.
@@ -1293,5 +1294,61 @@
12931294
12941295
blockquote.file-content {
12951296
/* file content block in the /file page */
12961297
margin: 0 1em;
12971298
}
1299
+
1300
+
1301
+/**
1302
+ Circular "help" buttons intended to be placed to the right of
1303
+ another element and hold text text for it. These typically get
1304
+ initialized automatically at page startup via
1305
+ fossil.popupwidget.js, and can be manually initialized/created
1306
+ using window.fossil.helpButtonlets.setup/create(). All of their
1307
+ child content (plain text and/or DOM elements) gets moved out of
1308
+ the DOM and shown in a singleton popup when they are clicked. They
1309
+ may be SPAN elements if their children are all inline elements,
1310
+ otherwise they must be DIVs (block elements) so that nesting of
1311
+ block-element content is legal.
1312
+*/
1313
+.help-buttonlet {
1314
+ display: inline-block;
1315
+ min-width: 1rem;
1316
+ max-width: 1rem;
1317
+ min-height: 1rem;
1318
+ max-height: 1rem;
1319
+ font-size: 0.9em;
1320
+ border-radius: 0.5rem;
1321
+ background-color: rgba(54, 54, 255,1);
1322
+ color: rgb(255, 255, 255);
1323
+ cursor: pointer;
1324
+ font-family: monspace;
1325
+ text-align: center;
1326
+ margin: 0 0 0 0.35em;
1327
+ border-width: 1px;
1328
+ border-style: outset;
1329
+ font-weight: 700;
1330
+ overflow: hidden;
1331
+}
1332
+
1333
+.help-buttonlet::before {
1334
+ content: "?";
1335
+}
1336
+
1337
+/**
1338
+ We really want to hide all help text via CSS but CSS cannot select
1339
+ TEXT nodes. Thus we move them out of the way programmatically
1340
+ during initialization.
1341
+*/
1342
+.help-buttonlet > *{}
1343
+
1344
+/**
1345
+ CSS class for PopupWidget which wraps .help-buttonlet content.
1346
+ They also have class fossil-tooltip. We need an overly-exact
1347
+ selector here to be certain that this class's style overrides
1348
+ that of fossil-tooltip.
1349
+*/
1350
+.fossil-tooltip.help-buttonlet-content {
1351
+ cursor: default;
1352
+ text-align: left;
1353
+ border-style: outset;
1354
+}
12981355
--- src/default.css
+++ src/default.css
@@ -1100,15 +1100,16 @@
1100 .font-size-200 {
1101 font-size: 200%;
1102 }
1103
1104 /**
1105 .input-with-label is intended to be a wrapper element which
1106 contain both a LABEL tag and an INPUT or SELECT control.
1107 The wrapper is "necessary", as opposed to placing the INPUT
1108 in the LABEL, so that we can include multiple INPUT
1109 elements (e.g. a set of radio buttons).
 
1110 */
1111 .input-with-label {
1112 border: 1px inset #808080;
1113 border-radius: 0.25em;
1114 padding: 0.25em 0.4em;
@@ -1251,11 +1252,11 @@
1251 border: 1px solid black;
1252 border-radius: 0.25em;
1253 position: absolute;
1254 display: inline-block;
1255 z-index: 19/*below default skin's hamburger popup*/;
1256 box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.75);
1257 background-color: inherit;
1258 }
1259
1260 .fossil-toast-message {
1261 /* "toast"-style popup message.
@@ -1293,5 +1294,61 @@
1293
1294 blockquote.file-content {
1295 /* file content block in the /file page */
1296 margin: 0 1em;
1297 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1298
--- src/default.css
+++ src/default.css
@@ -1100,15 +1100,16 @@
1100 .font-size-200 {
1101 font-size: 200%;
1102 }
1103
1104 /**
1105 .input-with-label is intended to be a wrapper element which contain
1106 both a LABEL tag and an INPUT or SELECT control. The wrapper is
1107 "necessary", as opposed to placing the INPUT in the LABEL, so that
1108 we can include multiple INPUT elements (e.g. a set of radio
1109 buttons). Note that these elements must sometimes be BLOCK elements
1110 (e.g. DIV) so that certain nesting constructs are legal.
1111 */
1112 .input-with-label {
1113 border: 1px inset #808080;
1114 border-radius: 0.25em;
1115 padding: 0.25em 0.4em;
@@ -1251,11 +1252,11 @@
1252 border: 1px solid black;
1253 border-radius: 0.25em;
1254 position: absolute;
1255 display: inline-block;
1256 z-index: 19/*below default skin's hamburger popup*/;
1257 box-shadow: -0.15em 0.15em 0.2em rgba(0, 0, 0, 0.75);
1258 background-color: inherit;
1259 }
1260
1261 .fossil-toast-message {
1262 /* "toast"-style popup message.
@@ -1293,5 +1294,61 @@
1294
1295 blockquote.file-content {
1296 /* file content block in the /file page */
1297 margin: 0 1em;
1298 }
1299
1300
1301 /**
1302 Circular "help" buttons intended to be placed to the right of
1303 another element and hold text text for it. These typically get
1304 initialized automatically at page startup via
1305 fossil.popupwidget.js, and can be manually initialized/created
1306 using window.fossil.helpButtonlets.setup/create(). All of their
1307 child content (plain text and/or DOM elements) gets moved out of
1308 the DOM and shown in a singleton popup when they are clicked. They
1309 may be SPAN elements if their children are all inline elements,
1310 otherwise they must be DIVs (block elements) so that nesting of
1311 block-element content is legal.
1312 */
1313 .help-buttonlet {
1314 display: inline-block;
1315 min-width: 1rem;
1316 max-width: 1rem;
1317 min-height: 1rem;
1318 max-height: 1rem;
1319 font-size: 0.9em;
1320 border-radius: 0.5rem;
1321 background-color: rgba(54, 54, 255,1);
1322 color: rgb(255, 255, 255);
1323 cursor: pointer;
1324 font-family: monspace;
1325 text-align: center;
1326 margin: 0 0 0 0.35em;
1327 border-width: 1px;
1328 border-style: outset;
1329 font-weight: 700;
1330 overflow: hidden;
1331 }
1332
1333 .help-buttonlet::before {
1334 content: "?";
1335 }
1336
1337 /**
1338 We really want to hide all help text via CSS but CSS cannot select
1339 TEXT nodes. Thus we move them out of the way programmatically
1340 during initialization.
1341 */
1342 .help-buttonlet > *{}
1343
1344 /**
1345 CSS class for PopupWidget which wraps .help-buttonlet content.
1346 They also have class fossil-tooltip. We need an overly-exact
1347 selector here to be certain that this class's style overrides
1348 that of fossil-tooltip.
1349 */
1350 .fossil-tooltip.help-buttonlet-content {
1351 cursor: default;
1352 text-align: left;
1353 border-style: outset;
1354 }
1355
--- src/descendants.c
+++ src/descendants.c
@@ -344,13 +344,13 @@
344344
** Find all leaf descendants of the check-in specified or if the argument
345345
** is omitted, of the check-in currently checked out.
346346
**
347347
** Options:
348348
** -R|--repository FILE Extract info from repository FILE
349
-** -W|--width <num> Width of lines (default is to auto-detect).
350
-** Must be >20 or 0 (= no limit, resulting in a
351
-** single line per entry).
349
+** -W|--width N Width of lines (default is to auto-detect).
350
+** Must be greater than 20 or else 0 for no
351
+** limit, resulting in a one line per entry.
352352
**
353353
** See also: [[finfo]], [[info]], [[leaves]]
354354
*/
355355
void descendants_cmd(void){
356356
Stmt q;
@@ -404,13 +404,13 @@
404404
** -a|--all show ALL leaves
405405
** --bybranch order output by branch name
406406
** -c|--closed show only closed leaves
407407
** -m|--multiple show only cases with multiple leaves on a single branch
408408
** --recompute recompute the "leaf" table in the repository DB
409
-** -W|--width <num> Width of lines (default is to auto-detect). Must be
410
-** >39 or 0 (= no limit, resulting in a single line per
411
-** entry).
409
+** -W|--width N Width of lines (default is to auto-detect). Must be
410
+** more than 39 or else 0 no limit, resulting in a single
411
+** line per entry.
412412
**
413413
** See also: [[descendants]], [[finfo]], [[info]], [[branch]]
414414
*/
415415
void leaves_cmd(void){
416416
Stmt q;
417417
--- src/descendants.c
+++ src/descendants.c
@@ -344,13 +344,13 @@
344 ** Find all leaf descendants of the check-in specified or if the argument
345 ** is omitted, of the check-in currently checked out.
346 **
347 ** Options:
348 ** -R|--repository FILE Extract info from repository FILE
349 ** -W|--width <num> Width of lines (default is to auto-detect).
350 ** Must be >20 or 0 (= no limit, resulting in a
351 ** single line per entry).
352 **
353 ** See also: [[finfo]], [[info]], [[leaves]]
354 */
355 void descendants_cmd(void){
356 Stmt q;
@@ -404,13 +404,13 @@
404 ** -a|--all show ALL leaves
405 ** --bybranch order output by branch name
406 ** -c|--closed show only closed leaves
407 ** -m|--multiple show only cases with multiple leaves on a single branch
408 ** --recompute recompute the "leaf" table in the repository DB
409 ** -W|--width <num> Width of lines (default is to auto-detect). Must be
410 ** >39 or 0 (= no limit, resulting in a single line per
411 ** entry).
412 **
413 ** See also: [[descendants]], [[finfo]], [[info]], [[branch]]
414 */
415 void leaves_cmd(void){
416 Stmt q;
417
--- src/descendants.c
+++ src/descendants.c
@@ -344,13 +344,13 @@
344 ** Find all leaf descendants of the check-in specified or if the argument
345 ** is omitted, of the check-in currently checked out.
346 **
347 ** Options:
348 ** -R|--repository FILE Extract info from repository FILE
349 ** -W|--width N Width of lines (default is to auto-detect).
350 ** Must be greater than 20 or else 0 for no
351 ** limit, resulting in a one line per entry.
352 **
353 ** See also: [[finfo]], [[info]], [[leaves]]
354 */
355 void descendants_cmd(void){
356 Stmt q;
@@ -404,13 +404,13 @@
404 ** -a|--all show ALL leaves
405 ** --bybranch order output by branch name
406 ** -c|--closed show only closed leaves
407 ** -m|--multiple show only cases with multiple leaves on a single branch
408 ** --recompute recompute the "leaf" table in the repository DB
409 ** -W|--width N Width of lines (default is to auto-detect). Must be
410 ** more than 39 or else 0 no limit, resulting in a single
411 ** line per entry.
412 **
413 ** See also: [[descendants]], [[finfo]], [[info]], [[branch]]
414 */
415 void leaves_cmd(void){
416 Stmt q;
417
+25 -24
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -815,34 +815,35 @@
815815
** The "--binary" option causes files matching the glob PATTERN to be treated
816816
** as binary when considering if they should be used with external diff program.
817817
** This option overrides the "binary-glob" setting.
818818
**
819819
** Options:
820
-** --binary PATTERN Treat files that match the glob PATTERN as binary
821
-** --branch BRANCH Show diff of all changes on BRANCH
822
-** --brief Show filenames only
823
-** --checkin VERSION Show diff of all changes in VERSION
824
-** --command PROG External diff program - overrides "diff-command"
825
-** --context|-c N Use N lines of context
826
-** --diff-binary BOOL Include binary files when using external commands
827
-** --exec-abs-paths Force absolute path names with external commands.
828
-** --exec-rel-paths Force relative path names with external commands.
829
-** --from|-r VERSION Select VERSION as source for the diff
830
-** --internal|-i Use internal diff logic
831
-** --new-file|-N Show complete text of added and deleted files
832
-** --numstat Show only the number of lines delete and added
833
-** --side-by-side|-y Side-by-side diff
834
-** --strip-trailing-cr Strip trailing CR
835
-** --tclsh PATH Tcl/Tk used for --tk (default: "tclsh")
836
-** --tk Launch a Tcl/Tk GUI for display
837
-** --to VERSION Select VERSION as target for the diff
838
-** --undo Diff against the "undo" buffer
839
-** --unified Unified diff
840
-** -v|--verbose Output complete text of added or deleted files
841
-** -w|--ignore-all-space Ignore white space when comparing lines
842
-** -W|--width <num> Width of lines in side-by-side diff
843
-** -Z|--ignore-trailing-space Ignore changes to end-of-line whitespace
820
+** --binary PATTERN Treat files that match the glob PATTERN
821
+** as binary
822
+** --branch BRANCH Show diff of all changes on BRANCH
823
+** --brief Show filenames only
824
+** --checkin VERSION Show diff of all changes in VERSION
825
+** --command PROG External diff program. Overrides "diff-command"
826
+** --context|-c N Use N lines of context
827
+** --diff-binary BOOL Include binary files with external commands
828
+** --exec-abs-paths Force absolute path names on external commands
829
+** --exec-rel-paths Force relative path names on external commands
830
+** --from|-r VERSION Select VERSION as source for the diff
831
+** --internal|-i Use internal diff logic
832
+** --new-file|-N Show complete text of added and deleted files
833
+** --numstat Show only the number of lines delete and added
834
+** --side-by-side|-y Side-by-side diff
835
+** --strip-trailing-cr Strip trailing CR
836
+** --tclsh PATH Tcl/Tk used for --tk (default: "tclsh")
837
+** --tk Launch a Tcl/Tk GUI for display
838
+** --to VERSION Select VERSION as target for the diff
839
+** --undo Diff against the "undo" buffer
840
+** --unified Unified diff
841
+** -v|--verbose Output complete text of added or deleted files
842
+** -w|--ignore-all-space Ignore white space when comparing lines
843
+** -W|--width N Width of lines in side-by-side diff
844
+** -Z|--ignore-trailing-space Ignore changes to end-of-line whitespace
844845
*/
845846
void diff_cmd(void){
846847
int isGDiff; /* True for gdiff. False for normal diff */
847848
int isInternDiff; /* True for internal diff */
848849
int verboseFlag; /* True if -v or --verbose flag is used */
849850
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -815,34 +815,35 @@
815 ** The "--binary" option causes files matching the glob PATTERN to be treated
816 ** as binary when considering if they should be used with external diff program.
817 ** This option overrides the "binary-glob" setting.
818 **
819 ** Options:
820 ** --binary PATTERN Treat files that match the glob PATTERN as binary
821 ** --branch BRANCH Show diff of all changes on BRANCH
822 ** --brief Show filenames only
823 ** --checkin VERSION Show diff of all changes in VERSION
824 ** --command PROG External diff program - overrides "diff-command"
825 ** --context|-c N Use N lines of context
826 ** --diff-binary BOOL Include binary files when using external commands
827 ** --exec-abs-paths Force absolute path names with external commands.
828 ** --exec-rel-paths Force relative path names with external commands.
829 ** --from|-r VERSION Select VERSION as source for the diff
830 ** --internal|-i Use internal diff logic
831 ** --new-file|-N Show complete text of added and deleted files
832 ** --numstat Show only the number of lines delete and added
833 ** --side-by-side|-y Side-by-side diff
834 ** --strip-trailing-cr Strip trailing CR
835 ** --tclsh PATH Tcl/Tk used for --tk (default: "tclsh")
836 ** --tk Launch a Tcl/Tk GUI for display
837 ** --to VERSION Select VERSION as target for the diff
838 ** --undo Diff against the "undo" buffer
839 ** --unified Unified diff
840 ** -v|--verbose Output complete text of added or deleted files
841 ** -w|--ignore-all-space Ignore white space when comparing lines
842 ** -W|--width <num> Width of lines in side-by-side diff
843 ** -Z|--ignore-trailing-space Ignore changes to end-of-line whitespace
 
844 */
845 void diff_cmd(void){
846 int isGDiff; /* True for gdiff. False for normal diff */
847 int isInternDiff; /* True for internal diff */
848 int verboseFlag; /* True if -v or --verbose flag is used */
849
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -815,34 +815,35 @@
815 ** The "--binary" option causes files matching the glob PATTERN to be treated
816 ** as binary when considering if they should be used with external diff program.
817 ** This option overrides the "binary-glob" setting.
818 **
819 ** Options:
820 ** --binary PATTERN Treat files that match the glob PATTERN
821 ** as binary
822 ** --branch BRANCH Show diff of all changes on BRANCH
823 ** --brief Show filenames only
824 ** --checkin VERSION Show diff of all changes in VERSION
825 ** --command PROG External diff program. Overrides "diff-command"
826 ** --context|-c N Use N lines of context
827 ** --diff-binary BOOL Include binary files with external commands
828 ** --exec-abs-paths Force absolute path names on external commands
829 ** --exec-rel-paths Force relative path names on external commands
830 ** --from|-r VERSION Select VERSION as source for the diff
831 ** --internal|-i Use internal diff logic
832 ** --new-file|-N Show complete text of added and deleted files
833 ** --numstat Show only the number of lines delete and added
834 ** --side-by-side|-y Side-by-side diff
835 ** --strip-trailing-cr Strip trailing CR
836 ** --tclsh PATH Tcl/Tk used for --tk (default: "tclsh")
837 ** --tk Launch a Tcl/Tk GUI for display
838 ** --to VERSION Select VERSION as target for the diff
839 ** --undo Diff against the "undo" buffer
840 ** --unified Unified diff
841 ** -v|--verbose Output complete text of added or deleted files
842 ** -w|--ignore-all-space Ignore white space when comparing lines
843 ** -W|--width N Width of lines in side-by-side diff
844 ** -Z|--ignore-trailing-space Ignore changes to end-of-line whitespace
845 */
846 void diff_cmd(void){
847 int isGDiff; /* True for gdiff. False for normal diff */
848 int isInternDiff; /* True for internal diff */
849 int verboseFlag; /* True if -v or --verbose flag is used */
850
+22 -14
--- src/fileedit.c
+++ src/fileedit.c
@@ -1705,16 +1705,20 @@
17051705
"data-tab-parent='fileedit-tabs' "
17061706
"data-tab-label='File Content' "
17071707
"class='hidden'"
17081708
">");
17091709
CX("<div class='flex-container flex-row child-gap-small'>");
1710
- CX("<button class='fileedit-content-reload confirmer' "
1711
- "title='Reload the file from the server, discarding "
1710
+ CX("<div class='input-with-label'>"
1711
+ "<button class='fileedit-content-reload confirmer' "
1712
+ ">Discard &amp; Reload</button>"
1713
+ "<div class='help-buttonlet'>"
1714
+ "Reload the file from the server, discarding "
17121715
"any local edits. To help avoid accidental loss of "
17131716
"edits, it requires confirmation (a second click) within "
1714
- "a few seconds or it will not reload.'"
1715
- ">Discard &amp; Reload</button>");
1717
+ "a few seconds or it will not reload."
1718
+ "</div>"
1719
+ "</div>");
17161720
style_select_list_int("select-font-size",
17171721
"editor_font_size", "Editor font size",
17181722
NULL/*tooltip*/,
17191723
100,
17201724
"100%", 100, "125%", 125,
@@ -1746,16 +1750,19 @@
17461750
/* ^^^ fossil.page[methodName](content, callback) */
17471751
"data-f-preview-to='#fileedit-tab-preview-wrapper' "
17481752
/* ^^^ dest elem ID */
17491753
">Refresh</button>");
17501754
/* Toggle auto-update of preview when the Preview tab is selected. */
1751
- style_labeled_checkbox("cb-preview-autoupdate",
1752
- NULL,
1753
- "Auto-refresh?",
1754
- "1", 1,
1755
- "If on, the preview will automatically "
1756
- "refresh when this tab is selected.");
1755
+ CX("<div class='input-with-label'>"
1756
+ "<input type='checkbox' value='1' "
1757
+ "id='cb-preview-autorefresh' checked>"
1758
+ "<label for='cb-preview-autorefresh'>Auto-refresh?</label>"
1759
+ "<div class='help-buttonlet'>"
1760
+ "If on, the preview will automatically "
1761
+ "refresh (if needed) when this tab is selected."
1762
+ "</div>"
1763
+ "</div>");
17571764
17581765
/* Default preview rendering mode selection... */
17591766
previewRenderMode = zFileMime
17601767
? ajax_render_mode_for_mimetype(zFileMime)
17611768
: AJAX_RENDER_GUESS;
@@ -1979,23 +1986,24 @@
19791986
"file/check-in combination are discarded.</li>");
19801987
CX("</ul>");
19811988
}
19821989
CX("</div>"/*#fileedit-tab-help*/);
19831990
1984
- builtin_request_js("sbsdiff.js");
1985
- style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer",
1986
- "storage", 0);
1987
- builtin_fulfill_js_requests();
1991
+ if(!builtin_bundle_all_fossil_js_apis()){
1992
+ builtin_emit_fossil_js_apis("fetch", "dom", "tabs", "confirmer",
1993
+ "storage", "popupwidget", 0);
1994
+ }
19881995
/*
19891996
** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is
19901997
** used for dynamically toggling certain UI components on and off.
19911998
** Must come after window.fossil has been intialized and before
19921999
** fossil.page.fileedit.js. Potential TODO: move this into the
19932000
** window.fossil bootstrapping so that we don't have to "fulfill"
19942001
** the JS multiple times.
19952002
*/
19962003
ajax_emit_js_preview_modes(1);
2004
+ builtin_request_js("sbsdiff.js");
19972005
builtin_request_js("fossil.page.fileedit.js");
19982006
builtin_fulfill_js_requests();
19992007
{
20002008
/* Dynamically populate the editor, display any error in the err
20012009
** blob, and/or switch to tab #0, where the file selector
20022010
--- src/fileedit.c
+++ src/fileedit.c
@@ -1705,16 +1705,20 @@
1705 "data-tab-parent='fileedit-tabs' "
1706 "data-tab-label='File Content' "
1707 "class='hidden'"
1708 ">");
1709 CX("<div class='flex-container flex-row child-gap-small'>");
1710 CX("<button class='fileedit-content-reload confirmer' "
1711 "title='Reload the file from the server, discarding "
 
 
 
1712 "any local edits. To help avoid accidental loss of "
1713 "edits, it requires confirmation (a second click) within "
1714 "a few seconds or it will not reload.'"
1715 ">Discard &amp; Reload</button>");
 
1716 style_select_list_int("select-font-size",
1717 "editor_font_size", "Editor font size",
1718 NULL/*tooltip*/,
1719 100,
1720 "100%", 100, "125%", 125,
@@ -1746,16 +1750,19 @@
1746 /* ^^^ fossil.page[methodName](content, callback) */
1747 "data-f-preview-to='#fileedit-tab-preview-wrapper' "
1748 /* ^^^ dest elem ID */
1749 ">Refresh</button>");
1750 /* Toggle auto-update of preview when the Preview tab is selected. */
1751 style_labeled_checkbox("cb-preview-autoupdate",
1752 NULL,
1753 "Auto-refresh?",
1754 "1", 1,
1755 "If on, the preview will automatically "
1756 "refresh when this tab is selected.");
 
 
 
1757
1758 /* Default preview rendering mode selection... */
1759 previewRenderMode = zFileMime
1760 ? ajax_render_mode_for_mimetype(zFileMime)
1761 : AJAX_RENDER_GUESS;
@@ -1979,23 +1986,24 @@
1979 "file/check-in combination are discarded.</li>");
1980 CX("</ul>");
1981 }
1982 CX("</div>"/*#fileedit-tab-help*/);
1983
1984 builtin_request_js("sbsdiff.js");
1985 style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer",
1986 "storage", 0);
1987 builtin_fulfill_js_requests();
1988 /*
1989 ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is
1990 ** used for dynamically toggling certain UI components on and off.
1991 ** Must come after window.fossil has been intialized and before
1992 ** fossil.page.fileedit.js. Potential TODO: move this into the
1993 ** window.fossil bootstrapping so that we don't have to "fulfill"
1994 ** the JS multiple times.
1995 */
1996 ajax_emit_js_preview_modes(1);
 
1997 builtin_request_js("fossil.page.fileedit.js");
1998 builtin_fulfill_js_requests();
1999 {
2000 /* Dynamically populate the editor, display any error in the err
2001 ** blob, and/or switch to tab #0, where the file selector
2002
--- src/fileedit.c
+++ src/fileedit.c
@@ -1705,16 +1705,20 @@
1705 "data-tab-parent='fileedit-tabs' "
1706 "data-tab-label='File Content' "
1707 "class='hidden'"
1708 ">");
1709 CX("<div class='flex-container flex-row child-gap-small'>");
1710 CX("<div class='input-with-label'>"
1711 "<button class='fileedit-content-reload confirmer' "
1712 ">Discard &amp; Reload</button>"
1713 "<div class='help-buttonlet'>"
1714 "Reload the file from the server, discarding "
1715 "any local edits. To help avoid accidental loss of "
1716 "edits, it requires confirmation (a second click) within "
1717 "a few seconds or it will not reload."
1718 "</div>"
1719 "</div>");
1720 style_select_list_int("select-font-size",
1721 "editor_font_size", "Editor font size",
1722 NULL/*tooltip*/,
1723 100,
1724 "100%", 100, "125%", 125,
@@ -1746,16 +1750,19 @@
1750 /* ^^^ fossil.page[methodName](content, callback) */
1751 "data-f-preview-to='#fileedit-tab-preview-wrapper' "
1752 /* ^^^ dest elem ID */
1753 ">Refresh</button>");
1754 /* Toggle auto-update of preview when the Preview tab is selected. */
1755 CX("<div class='input-with-label'>"
1756 "<input type='checkbox' value='1' "
1757 "id='cb-preview-autorefresh' checked>"
1758 "<label for='cb-preview-autorefresh'>Auto-refresh?</label>"
1759 "<div class='help-buttonlet'>"
1760 "If on, the preview will automatically "
1761 "refresh (if needed) when this tab is selected."
1762 "</div>"
1763 "</div>");
1764
1765 /* Default preview rendering mode selection... */
1766 previewRenderMode = zFileMime
1767 ? ajax_render_mode_for_mimetype(zFileMime)
1768 : AJAX_RENDER_GUESS;
@@ -1979,23 +1986,24 @@
1986 "file/check-in combination are discarded.</li>");
1987 CX("</ul>");
1988 }
1989 CX("</div>"/*#fileedit-tab-help*/);
1990
1991 if(!builtin_bundle_all_fossil_js_apis()){
1992 builtin_emit_fossil_js_apis("fetch", "dom", "tabs", "confirmer",
1993 "storage", "popupwidget", 0);
1994 }
1995 /*
1996 ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is
1997 ** used for dynamically toggling certain UI components on and off.
1998 ** Must come after window.fossil has been intialized and before
1999 ** fossil.page.fileedit.js. Potential TODO: move this into the
2000 ** window.fossil bootstrapping so that we don't have to "fulfill"
2001 ** the JS multiple times.
2002 */
2003 ajax_emit_js_preview_modes(1);
2004 builtin_request_js("sbsdiff.js");
2005 builtin_request_js("fossil.page.fileedit.js");
2006 builtin_fulfill_js_requests();
2007 {
2008 /* Dynamically populate the editor, display any error in the err
2009 ** blob, and/or switch to tab #0, where the file selector
2010
+3 -4
--- src/finfo.c
+++ src/finfo.c
@@ -44,19 +44,18 @@
4444
** -b|--brief display a brief (one line / revision) summary
4545
** --case-sensitive B Enable or disable case-sensitive filenames. B is a
4646
** boolean: "yes", "no", "true", "false", etc.
4747
** -l|--log select log mode (the default)
4848
** -n|--limit N Display the first N changes (default unlimited).
49
-** N<=0 means no limit.
49
+** N less than 0 means no limit.
5050
** --offset P skip P changes
5151
** -p|--print select print mode
5252
** -r|--revision R print the given revision (or ckout, if none is given)
5353
** to stdout (only in print mode)
5454
** -s|--status select status mode (print a status indicator for FILE)
55
-** -W|--width <num> Width of lines (default is to auto-detect). Must be
56
-** >22 or 0 (= no limit, resulting in a single line per
57
-** entry).
55
+** -W|--width N Width of lines (default is to auto-detect). Must be
56
+** more than 22 or else 0 to indicate no limit.
5857
**
5958
** See also: [[artifact]], [[cat]], [[descendants]], [[info]], [[leaves]]
6059
*/
6160
void finfo_cmd(void){
6261
db_must_be_within_tree();
6362
--- src/finfo.c
+++ src/finfo.c
@@ -44,19 +44,18 @@
44 ** -b|--brief display a brief (one line / revision) summary
45 ** --case-sensitive B Enable or disable case-sensitive filenames. B is a
46 ** boolean: "yes", "no", "true", "false", etc.
47 ** -l|--log select log mode (the default)
48 ** -n|--limit N Display the first N changes (default unlimited).
49 ** N<=0 means no limit.
50 ** --offset P skip P changes
51 ** -p|--print select print mode
52 ** -r|--revision R print the given revision (or ckout, if none is given)
53 ** to stdout (only in print mode)
54 ** -s|--status select status mode (print a status indicator for FILE)
55 ** -W|--width <num> Width of lines (default is to auto-detect). Must be
56 ** >22 or 0 (= no limit, resulting in a single line per
57 ** entry).
58 **
59 ** See also: [[artifact]], [[cat]], [[descendants]], [[info]], [[leaves]]
60 */
61 void finfo_cmd(void){
62 db_must_be_within_tree();
63
--- src/finfo.c
+++ src/finfo.c
@@ -44,19 +44,18 @@
44 ** -b|--brief display a brief (one line / revision) summary
45 ** --case-sensitive B Enable or disable case-sensitive filenames. B is a
46 ** boolean: "yes", "no", "true", "false", etc.
47 ** -l|--log select log mode (the default)
48 ** -n|--limit N Display the first N changes (default unlimited).
49 ** N less than 0 means no limit.
50 ** --offset P skip P changes
51 ** -p|--print select print mode
52 ** -r|--revision R print the given revision (or ckout, if none is given)
53 ** to stdout (only in print mode)
54 ** -s|--status select status mode (print a status indicator for FILE)
55 ** -W|--width N Width of lines (default is to auto-detect). Must be
56 ** more than 22 or else 0 to indicate no limit.
 
57 **
58 ** See also: [[artifact]], [[cat]], [[descendants]], [[info]], [[leaves]]
59 */
60 void finfo_cmd(void){
61 db_must_be_within_tree();
62
+69 -23
--- src/forum.c
+++ src/forum.c
@@ -35,10 +35,12 @@
3535
struct ForumPost {
3636
int fpid; /* rid for this post */
3737
int sid; /* Serial ID number */
3838
int rev; /* Revision number */
3939
char *zUuid; /* Artifact hash */
40
+ char *zDisplayName; /* Name of user who wrote this post */
41
+ double rDate; /* Date for this post */
4042
ForumPost *pIrt; /* This post replies to pIrt */
4143
ForumPost *pEditHead; /* Original, unedited post */
4244
ForumPost *pEditTail; /* Most recent edit for this post */
4345
ForumPost *pEditNext; /* This post is edited by pEditNext */
4446
ForumPost *pEditPrev; /* This post is an edit of pEditPrev */
@@ -84,10 +86,11 @@
8486
static void forumthread_delete(ForumThread *pThread){
8587
ForumPost *pPost, *pNext;
8688
for(pPost=pThread->pFirst; pPost; pPost = pNext){
8789
pNext = pPost->pNext;
8890
fossil_free(pPost->zUuid);
91
+ fossil_free(pPost->zDisplayName);
8992
fossil_free(pPost);
9093
}
9194
fossil_free(pThread);
9295
}
9396
@@ -163,11 +166,11 @@
163166
int sid = 1;
164167
int firt, fprev;
165168
pThread = fossil_malloc( sizeof(*pThread) );
166169
memset(pThread, 0, sizeof(*pThread));
167170
db_prepare(&q,
168
- "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid)"
171
+ "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid), fmtime"
169172
" FROM forumpost"
170173
" WHERE froot=%d ORDER BY fmtime",
171174
froot
172175
);
173176
while( db_step(&q)==SQLITE_ROW ){
@@ -175,10 +178,11 @@
175178
memset(pPost, 0, sizeof(*pPost));
176179
pPost->fpid = db_column_int(&q, 0);
177180
firt = db_column_int(&q, 1);
178181
fprev = db_column_int(&q, 2);
179182
pPost->zUuid = fossil_strdup(db_column_text(&q,3));
183
+ pPost->rDate = db_column_double(&q,4);
180184
if( !fprev ) pPost->sid = sid++;
181185
pPost->pPrev = pThread->pLast;
182186
pPost->pNext = 0;
183187
if( pThread->pLast==0 ){
184188
pThread->pFirst = pPost;
@@ -367,11 +371,11 @@
367371
** name just be the login.
368372
**
369373
** Space to hold the returned name is obtained from fossil_strdup() or
370374
** mprintf() and should be freed by the caller.
371375
*/
372
-char *display_name_from_login(const char *zLogin){
376
+static char *display_name_from_login(const char *zLogin){
373377
static Stmt q;
374378
char *zResult;
375379
db_static_prepare(&q,
376380
"SELECT display_name(info) FROM user WHERE login=$login"
377381
);
@@ -387,10 +391,34 @@
387391
zResult = fossil_strdup(zLogin);
388392
}
389393
db_reset(&q);
390394
return zResult;
391395
}
396
+
397
+/*
398
+** Compute and return the display name for a ForumPost. If
399
+** pManifest is not NULL, then it is a Manifest object for the post.
400
+** if pManifest is NULL, this routine has to fetch and parse the
401
+** Manifest object for itself.
402
+**
403
+** Memory to hold the display name is attached to p->zDisplayName
404
+** and will be freed together with the ForumPost object p when it
405
+** is freed.
406
+*/
407
+static char *forum_post_display_name(ForumPost *p, Manifest *pManifest){
408
+ Manifest *pToFree = 0;
409
+ if( p->zDisplayName ) return p->zDisplayName;
410
+ if( pManifest==0 ){
411
+ pManifest = pToFree = manifest_get(p->fpid, CFTYPE_FORUM, 0);
412
+ if( pManifest==0 ) return "(unknown)";
413
+ }
414
+ p->zDisplayName = display_name_from_login(pManifest->zUser);
415
+ if( pToFree ) manifest_destroy(pToFree);
416
+ if( p->zDisplayName==0 ) return "(unknown)";
417
+ return p->zDisplayName;
418
+}
419
+
392420
393421
/*
394422
** Display a single post in a forum thread.
395423
*/
396424
static void forum_display_post(
@@ -400,11 +428,12 @@
400428
int bUnf, /* True to leave the post unformatted */
401429
int bHist, /* True if showing edit history */
402430
int bSelect, /* True if this is the selected post */
403431
char *zQuery /* Common query string */
404432
){
405
- char *zDisplayName; /* The display name */
433
+ char *zPosterName; /* Name of user who originally made this post */
434
+ char *zEditorName; /* Name of user who provided the current edit */
406435
char *zDate; /* The time/date string */
407436
char *zHist; /* History query string */
408437
Manifest *pManifest; /* Manifest comprising the current post */
409438
int bPrivate; /* True for posts awaiting moderation */
410439
int bSameUser; /* True if author is also the reader */
@@ -431,29 +460,46 @@
431460
/* If this is the first post (or an edit thereof), emit the thread title. */
432461
if( pManifest->zThreadTitle ){
433462
@ <h1>%h(pManifest->zThreadTitle)</h1>
434463
}
435464
436
- /* Emit the serial number, revision number, author, and date. */
437
- zDisplayName = display_name_from_login(pManifest->zUser);
438
- zDate = db_text(0, "SELECT datetime(%.17g)", pManifest->rDate);
439
- @ <h3 class='forumPostHdr'>(%d(p->sid)\
440
- if( p->nEdit ){
441
- @ .%.*d(fossil_num_digits(p->nEdit))(p->rev)\
442
- }
443
- @ ) By %h(zDisplayName) on %h(zDate)
444
- fossil_free(zDisplayName);
465
+ /* Begin emitting the header line. The forum of the title
466
+ ** varies depending on whether:
467
+ ** * The post is uneditted
468
+ ** * The post was last editted by the original author
469
+ ** * The post was last editted by a different person
470
+ */
471
+ if( p->pEditHead ){
472
+ zDate = db_text(0, "SELECT datetime(%.17g)", p->pEditHead->rDate);
473
+ }else{
474
+ zPosterName = forum_post_display_name(p, pManifest);
475
+ zEditorName = zPosterName;
476
+ }
477
+ zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate);
478
+ if( p->pEditPrev ){
479
+ zPosterName = forum_post_display_name(p->pEditHead, 0);
480
+ zEditorName = forum_post_display_name(p, pManifest);
481
+ zHist = bHist ? "" : "&hist";
482
+ @ <h3 class='forumPostHdr'>(%d(p->sid)\
483
+ @ .%0*d(fossil_num_digits(p->nEdit))(p->rev)) \
484
+ if( fossil_strcmp(zPosterName, zEditorName)==0 ){
485
+ @ By %h(zPosterName) on %h(zDate) editted from \
486
+ @ %z(href("%R/forumpost/%S?%s%s",p->pEditPrev->zUuid,zQuery,zHist))\
487
+ @ %d(p->sid).%0*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
488
+ }else{
489
+ @ Originally by %h(zPosterName) \
490
+ @ with edits by %h(zEditorName) on %h(zDate) from \
491
+ @ %z(href("%R/forumpost/%S?%s%s",p->pEditPrev->zUuid,zQuery,zHist))\
492
+ @ %d(p->sid).%0*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
493
+ }
494
+ }else{
495
+ zPosterName = forum_post_display_name(p, pManifest);
496
+ @ <h3 class='forumPostHdr'>(%d(p->sid)) \
497
+ @ By %h(zPosterName) on %h(zDate)
498
+ }
445499
fossil_free(zDate);
446500
447
- /* If this is an edit, refer back to the old version. Be sure "hist" is in
448
- ** the query string so the old version will actually be shown. */
449
- if( p->pEditPrev ){
450
- zHist = bHist ? "" : "&hist";
451
- @ edit of \
452
- @ %z(href("%R/forumpost/%S?%s%s",p->pEditPrev->zUuid,zQuery,zHist))\
453
- @ %d(p->sid).%.*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
454
- }
455501
456502
/* If debugging is enabled, link to the artifact page. */
457503
if( g.perm.Debug ){
458504
@ <span class="debug">\
459505
@ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span>
@@ -462,20 +508,20 @@
462508
/* If this is a reply, refer back to the parent post. */
463509
if( p->pIrt ){
464510
@ in reply to %z(href("%R/forumpost/%S?%s",p->pIrt->zUuid,zQuery))\
465511
@ %d(p->pIrt->sid)\
466512
if( p->pIrt->nEdit ){
467
- @ .%.*d(fossil_num_digits(p->pIrt->nEdit))(p->pIrt->rev)\
513
+ @ .%0*d(fossil_num_digits(p->pIrt->nEdit))(p->pIrt->rev)\
468514
}
469515
@ </a>
470516
}
471517
472518
/* If this post was later edited, refer forward to the next edit. */
473519
if( p->pEditNext ){
474520
@ updated by %z(href("%R/forumpost/%S?%s",p->pEditNext->zUuid,zQuery))\
475521
@ %d(p->pEditNext->sid)\
476
- @ .%.*d(fossil_num_digits(p->nEdit))(p->pEditNext->rev)</a>
522
+ @ .%0*d(fossil_num_digits(p->nEdit))(p->pEditNext->rev)</a>
477523
}
478524
479525
/* Provide a link to select the individual post. */
480526
if( !bSelect ){
481527
@ %z(href("%R/forumpost/%S?%s",p->zUuid,zQuery))[link]</a>
@@ -804,11 +850,11 @@
804850
805851
/* Display the thread. */
806852
forum_display_thread(froot, fpid, mode, bUnf, bHist);
807853
808854
/* Emit Forum Javascript. */
809
- style_emit_script_fossil_bootstrap(1);
855
+ builtin_emit_script_fossil_bootstrap(1);
810856
builtin_request_js("forum.js");
811857
builtin_request_js("fossil.dom.js");
812858
builtin_request_js("fossil.page.forumpost.js");
813859
814860
/* Emit the page style. */
815861
--- src/forum.c
+++ src/forum.c
@@ -35,10 +35,12 @@
35 struct ForumPost {
36 int fpid; /* rid for this post */
37 int sid; /* Serial ID number */
38 int rev; /* Revision number */
39 char *zUuid; /* Artifact hash */
 
 
40 ForumPost *pIrt; /* This post replies to pIrt */
41 ForumPost *pEditHead; /* Original, unedited post */
42 ForumPost *pEditTail; /* Most recent edit for this post */
43 ForumPost *pEditNext; /* This post is edited by pEditNext */
44 ForumPost *pEditPrev; /* This post is an edit of pEditPrev */
@@ -84,10 +86,11 @@
84 static void forumthread_delete(ForumThread *pThread){
85 ForumPost *pPost, *pNext;
86 for(pPost=pThread->pFirst; pPost; pPost = pNext){
87 pNext = pPost->pNext;
88 fossil_free(pPost->zUuid);
 
89 fossil_free(pPost);
90 }
91 fossil_free(pThread);
92 }
93
@@ -163,11 +166,11 @@
163 int sid = 1;
164 int firt, fprev;
165 pThread = fossil_malloc( sizeof(*pThread) );
166 memset(pThread, 0, sizeof(*pThread));
167 db_prepare(&q,
168 "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid)"
169 " FROM forumpost"
170 " WHERE froot=%d ORDER BY fmtime",
171 froot
172 );
173 while( db_step(&q)==SQLITE_ROW ){
@@ -175,10 +178,11 @@
175 memset(pPost, 0, sizeof(*pPost));
176 pPost->fpid = db_column_int(&q, 0);
177 firt = db_column_int(&q, 1);
178 fprev = db_column_int(&q, 2);
179 pPost->zUuid = fossil_strdup(db_column_text(&q,3));
 
180 if( !fprev ) pPost->sid = sid++;
181 pPost->pPrev = pThread->pLast;
182 pPost->pNext = 0;
183 if( pThread->pLast==0 ){
184 pThread->pFirst = pPost;
@@ -367,11 +371,11 @@
367 ** name just be the login.
368 **
369 ** Space to hold the returned name is obtained from fossil_strdup() or
370 ** mprintf() and should be freed by the caller.
371 */
372 char *display_name_from_login(const char *zLogin){
373 static Stmt q;
374 char *zResult;
375 db_static_prepare(&q,
376 "SELECT display_name(info) FROM user WHERE login=$login"
377 );
@@ -387,10 +391,34 @@
387 zResult = fossil_strdup(zLogin);
388 }
389 db_reset(&q);
390 return zResult;
391 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
393 /*
394 ** Display a single post in a forum thread.
395 */
396 static void forum_display_post(
@@ -400,11 +428,12 @@
400 int bUnf, /* True to leave the post unformatted */
401 int bHist, /* True if showing edit history */
402 int bSelect, /* True if this is the selected post */
403 char *zQuery /* Common query string */
404 ){
405 char *zDisplayName; /* The display name */
 
406 char *zDate; /* The time/date string */
407 char *zHist; /* History query string */
408 Manifest *pManifest; /* Manifest comprising the current post */
409 int bPrivate; /* True for posts awaiting moderation */
410 int bSameUser; /* True if author is also the reader */
@@ -431,29 +460,46 @@
431 /* If this is the first post (or an edit thereof), emit the thread title. */
432 if( pManifest->zThreadTitle ){
433 @ <h1>%h(pManifest->zThreadTitle)</h1>
434 }
435
436 /* Emit the serial number, revision number, author, and date. */
437 zDisplayName = display_name_from_login(pManifest->zUser);
438 zDate = db_text(0, "SELECT datetime(%.17g)", pManifest->rDate);
439 @ <h3 class='forumPostHdr'>(%d(p->sid)\
440 if( p->nEdit ){
441 @ .%.*d(fossil_num_digits(p->nEdit))(p->rev)\
442 }
443 @ ) By %h(zDisplayName) on %h(zDate)
444 fossil_free(zDisplayName);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
445 fossil_free(zDate);
446
447 /* If this is an edit, refer back to the old version. Be sure "hist" is in
448 ** the query string so the old version will actually be shown. */
449 if( p->pEditPrev ){
450 zHist = bHist ? "" : "&hist";
451 @ edit of \
452 @ %z(href("%R/forumpost/%S?%s%s",p->pEditPrev->zUuid,zQuery,zHist))\
453 @ %d(p->sid).%.*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
454 }
455
456 /* If debugging is enabled, link to the artifact page. */
457 if( g.perm.Debug ){
458 @ <span class="debug">\
459 @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span>
@@ -462,20 +508,20 @@
462 /* If this is a reply, refer back to the parent post. */
463 if( p->pIrt ){
464 @ in reply to %z(href("%R/forumpost/%S?%s",p->pIrt->zUuid,zQuery))\
465 @ %d(p->pIrt->sid)\
466 if( p->pIrt->nEdit ){
467 @ .%.*d(fossil_num_digits(p->pIrt->nEdit))(p->pIrt->rev)\
468 }
469 @ </a>
470 }
471
472 /* If this post was later edited, refer forward to the next edit. */
473 if( p->pEditNext ){
474 @ updated by %z(href("%R/forumpost/%S?%s",p->pEditNext->zUuid,zQuery))\
475 @ %d(p->pEditNext->sid)\
476 @ .%.*d(fossil_num_digits(p->nEdit))(p->pEditNext->rev)</a>
477 }
478
479 /* Provide a link to select the individual post. */
480 if( !bSelect ){
481 @ %z(href("%R/forumpost/%S?%s",p->zUuid,zQuery))[link]</a>
@@ -804,11 +850,11 @@
804
805 /* Display the thread. */
806 forum_display_thread(froot, fpid, mode, bUnf, bHist);
807
808 /* Emit Forum Javascript. */
809 style_emit_script_fossil_bootstrap(1);
810 builtin_request_js("forum.js");
811 builtin_request_js("fossil.dom.js");
812 builtin_request_js("fossil.page.forumpost.js");
813
814 /* Emit the page style. */
815
--- src/forum.c
+++ src/forum.c
@@ -35,10 +35,12 @@
35 struct ForumPost {
36 int fpid; /* rid for this post */
37 int sid; /* Serial ID number */
38 int rev; /* Revision number */
39 char *zUuid; /* Artifact hash */
40 char *zDisplayName; /* Name of user who wrote this post */
41 double rDate; /* Date for this post */
42 ForumPost *pIrt; /* This post replies to pIrt */
43 ForumPost *pEditHead; /* Original, unedited post */
44 ForumPost *pEditTail; /* Most recent edit for this post */
45 ForumPost *pEditNext; /* This post is edited by pEditNext */
46 ForumPost *pEditPrev; /* This post is an edit of pEditPrev */
@@ -84,10 +86,11 @@
86 static void forumthread_delete(ForumThread *pThread){
87 ForumPost *pPost, *pNext;
88 for(pPost=pThread->pFirst; pPost; pPost = pNext){
89 pNext = pPost->pNext;
90 fossil_free(pPost->zUuid);
91 fossil_free(pPost->zDisplayName);
92 fossil_free(pPost);
93 }
94 fossil_free(pThread);
95 }
96
@@ -163,11 +166,11 @@
166 int sid = 1;
167 int firt, fprev;
168 pThread = fossil_malloc( sizeof(*pThread) );
169 memset(pThread, 0, sizeof(*pThread));
170 db_prepare(&q,
171 "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid), fmtime"
172 " FROM forumpost"
173 " WHERE froot=%d ORDER BY fmtime",
174 froot
175 );
176 while( db_step(&q)==SQLITE_ROW ){
@@ -175,10 +178,11 @@
178 memset(pPost, 0, sizeof(*pPost));
179 pPost->fpid = db_column_int(&q, 0);
180 firt = db_column_int(&q, 1);
181 fprev = db_column_int(&q, 2);
182 pPost->zUuid = fossil_strdup(db_column_text(&q,3));
183 pPost->rDate = db_column_double(&q,4);
184 if( !fprev ) pPost->sid = sid++;
185 pPost->pPrev = pThread->pLast;
186 pPost->pNext = 0;
187 if( pThread->pLast==0 ){
188 pThread->pFirst = pPost;
@@ -367,11 +371,11 @@
371 ** name just be the login.
372 **
373 ** Space to hold the returned name is obtained from fossil_strdup() or
374 ** mprintf() and should be freed by the caller.
375 */
376 static char *display_name_from_login(const char *zLogin){
377 static Stmt q;
378 char *zResult;
379 db_static_prepare(&q,
380 "SELECT display_name(info) FROM user WHERE login=$login"
381 );
@@ -387,10 +391,34 @@
391 zResult = fossil_strdup(zLogin);
392 }
393 db_reset(&q);
394 return zResult;
395 }
396
397 /*
398 ** Compute and return the display name for a ForumPost. If
399 ** pManifest is not NULL, then it is a Manifest object for the post.
400 ** if pManifest is NULL, this routine has to fetch and parse the
401 ** Manifest object for itself.
402 **
403 ** Memory to hold the display name is attached to p->zDisplayName
404 ** and will be freed together with the ForumPost object p when it
405 ** is freed.
406 */
407 static char *forum_post_display_name(ForumPost *p, Manifest *pManifest){
408 Manifest *pToFree = 0;
409 if( p->zDisplayName ) return p->zDisplayName;
410 if( pManifest==0 ){
411 pManifest = pToFree = manifest_get(p->fpid, CFTYPE_FORUM, 0);
412 if( pManifest==0 ) return "(unknown)";
413 }
414 p->zDisplayName = display_name_from_login(pManifest->zUser);
415 if( pToFree ) manifest_destroy(pToFree);
416 if( p->zDisplayName==0 ) return "(unknown)";
417 return p->zDisplayName;
418 }
419
420
421 /*
422 ** Display a single post in a forum thread.
423 */
424 static void forum_display_post(
@@ -400,11 +428,12 @@
428 int bUnf, /* True to leave the post unformatted */
429 int bHist, /* True if showing edit history */
430 int bSelect, /* True if this is the selected post */
431 char *zQuery /* Common query string */
432 ){
433 char *zPosterName; /* Name of user who originally made this post */
434 char *zEditorName; /* Name of user who provided the current edit */
435 char *zDate; /* The time/date string */
436 char *zHist; /* History query string */
437 Manifest *pManifest; /* Manifest comprising the current post */
438 int bPrivate; /* True for posts awaiting moderation */
439 int bSameUser; /* True if author is also the reader */
@@ -431,29 +460,46 @@
460 /* If this is the first post (or an edit thereof), emit the thread title. */
461 if( pManifest->zThreadTitle ){
462 @ <h1>%h(pManifest->zThreadTitle)</h1>
463 }
464
465 /* Begin emitting the header line. The forum of the title
466 ** varies depending on whether:
467 ** * The post is uneditted
468 ** * The post was last editted by the original author
469 ** * The post was last editted by a different person
470 */
471 if( p->pEditHead ){
472 zDate = db_text(0, "SELECT datetime(%.17g)", p->pEditHead->rDate);
473 }else{
474 zPosterName = forum_post_display_name(p, pManifest);
475 zEditorName = zPosterName;
476 }
477 zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate);
478 if( p->pEditPrev ){
479 zPosterName = forum_post_display_name(p->pEditHead, 0);
480 zEditorName = forum_post_display_name(p, pManifest);
481 zHist = bHist ? "" : "&hist";
482 @ <h3 class='forumPostHdr'>(%d(p->sid)\
483 @ .%0*d(fossil_num_digits(p->nEdit))(p->rev)) \
484 if( fossil_strcmp(zPosterName, zEditorName)==0 ){
485 @ By %h(zPosterName) on %h(zDate) editted from \
486 @ %z(href("%R/forumpost/%S?%s%s",p->pEditPrev->zUuid,zQuery,zHist))\
487 @ %d(p->sid).%0*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
488 }else{
489 @ Originally by %h(zPosterName) \
490 @ with edits by %h(zEditorName) on %h(zDate) from \
491 @ %z(href("%R/forumpost/%S?%s%s",p->pEditPrev->zUuid,zQuery,zHist))\
492 @ %d(p->sid).%0*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
493 }
494 }else{
495 zPosterName = forum_post_display_name(p, pManifest);
496 @ <h3 class='forumPostHdr'>(%d(p->sid)) \
497 @ By %h(zPosterName) on %h(zDate)
498 }
499 fossil_free(zDate);
500
 
 
 
 
 
 
 
 
501
502 /* If debugging is enabled, link to the artifact page. */
503 if( g.perm.Debug ){
504 @ <span class="debug">\
505 @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span>
@@ -462,20 +508,20 @@
508 /* If this is a reply, refer back to the parent post. */
509 if( p->pIrt ){
510 @ in reply to %z(href("%R/forumpost/%S?%s",p->pIrt->zUuid,zQuery))\
511 @ %d(p->pIrt->sid)\
512 if( p->pIrt->nEdit ){
513 @ .%0*d(fossil_num_digits(p->pIrt->nEdit))(p->pIrt->rev)\
514 }
515 @ </a>
516 }
517
518 /* If this post was later edited, refer forward to the next edit. */
519 if( p->pEditNext ){
520 @ updated by %z(href("%R/forumpost/%S?%s",p->pEditNext->zUuid,zQuery))\
521 @ %d(p->pEditNext->sid)\
522 @ .%0*d(fossil_num_digits(p->nEdit))(p->pEditNext->rev)</a>
523 }
524
525 /* Provide a link to select the individual post. */
526 if( !bSelect ){
527 @ %z(href("%R/forumpost/%S?%s",p->zUuid,zQuery))[link]</a>
@@ -804,11 +850,11 @@
850
851 /* Display the thread. */
852 forum_display_thread(froot, fpid, mode, bUnf, bHist);
853
854 /* Emit Forum Javascript. */
855 builtin_emit_script_fossil_bootstrap(1);
856 builtin_request_js("forum.js");
857 builtin_request_js("fossil.dom.js");
858 builtin_request_js("fossil.page.forumpost.js");
859
860 /* Emit the page style. */
861
--- src/fossil.bootstrap.js
+++ src/fossil.bootstrap.js
@@ -5,18 +5,18 @@
55
*/
66
if(typeof window.CustomEvent === "function") return false;
77
window.CustomEvent = function(event, params) {
88
if(!params) params = {bubbles: false, cancelable: false, detail: null};
99
const evt = document.createEvent('CustomEvent');
10
- evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
10
+ evt.initCustomEvent( event, !!params.bubbles, !!params.cancelable, params.detail );
1111
return evt;
1212
};
1313
})();
1414
(function(global){
15
- /* Bootstrapping bits for the global.fossil object. Must be
16
- loaded after style.c:style_emit_script_tag() has initialized
17
- that object.
15
+ /* Bootstrapping bits for the global.fossil object. Must be loaded
16
+ after style.c:builtin_emit_script_fossil_bootstrap() has
17
+ initialized that object.
1818
*/
1919
2020
const F = global.fossil;
2121
2222
/**
@@ -328,10 +328,18 @@
328328
*/
329329
F.onPageLoad = function(callback){
330330
window.addEventListener('load', callback, false);
331331
return this;
332332
};
333
+ /**
334
+ Convenience wrapper which adds a DOMContentLoadedevent listener
335
+ to the window object. Returns this.
336
+ */
337
+ F.onDOMContentLoaded = function(callback){
338
+ window.addEventListener('DOMContentLoaded', callback, false);
339
+ return this;
340
+ };
333341
334342
/**
335343
Assuming name is a repo-style filename, this function returns
336344
a shortened form of that name:
337345
338346
--- src/fossil.bootstrap.js
+++ src/fossil.bootstrap.js
@@ -5,18 +5,18 @@
5 */
6 if(typeof window.CustomEvent === "function") return false;
7 window.CustomEvent = function(event, params) {
8 if(!params) params = {bubbles: false, cancelable: false, detail: null};
9 const evt = document.createEvent('CustomEvent');
10 evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
11 return evt;
12 };
13 })();
14 (function(global){
15 /* Bootstrapping bits for the global.fossil object. Must be
16 loaded after style.c:style_emit_script_tag() has initialized
17 that object.
18 */
19
20 const F = global.fossil;
21
22 /**
@@ -328,10 +328,18 @@
328 */
329 F.onPageLoad = function(callback){
330 window.addEventListener('load', callback, false);
331 return this;
332 };
 
 
 
 
 
 
 
 
333
334 /**
335 Assuming name is a repo-style filename, this function returns
336 a shortened form of that name:
337
338
--- src/fossil.bootstrap.js
+++ src/fossil.bootstrap.js
@@ -5,18 +5,18 @@
5 */
6 if(typeof window.CustomEvent === "function") return false;
7 window.CustomEvent = function(event, params) {
8 if(!params) params = {bubbles: false, cancelable: false, detail: null};
9 const evt = document.createEvent('CustomEvent');
10 evt.initCustomEvent( event, !!params.bubbles, !!params.cancelable, params.detail );
11 return evt;
12 };
13 })();
14 (function(global){
15 /* Bootstrapping bits for the global.fossil object. Must be loaded
16 after style.c:builtin_emit_script_fossil_bootstrap() has
17 initialized that object.
18 */
19
20 const F = global.fossil;
21
22 /**
@@ -328,10 +328,18 @@
328 */
329 F.onPageLoad = function(callback){
330 window.addEventListener('load', callback, false);
331 return this;
332 };
333 /**
334 Convenience wrapper which adds a DOMContentLoadedevent listener
335 to the window object. Returns this.
336 */
337 F.onDOMContentLoaded = function(callback){
338 window.addEventListener('DOMContentLoaded', callback, false);
339 return this;
340 };
341
342 /**
343 Assuming name is a repo-style filename, this function returns
344 a shortened form of that name:
345
346
--- src/fossil.numbered-lines.js
+++ src/fossil.numbered-lines.js
@@ -6,19 +6,19 @@
66
77
Requires: fossil.bootstrap, fossil.dom, fossil.popupwidget,
88
fossil.copybutton
99
*/
1010
var tbl = arg || document.querySelectorAll('table.numbered-lines');
11
- if(!tbl) return /* no matching elements */;
12
- else if(!arg){
11
+ if(tbl && !arg){
1312
if(tbl.length>1){ /* multiple query results: recurse */
1413
tbl.forEach( (t)=>callee(t) );
1514
return;
1615
}else{/* single query result */
1716
tbl = tbl[0];
1817
}
1918
}
19
+ if(!tbl) return /* no matching elements */;
2020
const F = window.fossil, D = F.dom;
2121
const tdLn = tbl.querySelector('td.line-numbers');
2222
const lineState = {
2323
urlArgs: (window.location.search||'?')
2424
.replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
2525
--- src/fossil.numbered-lines.js
+++ src/fossil.numbered-lines.js
@@ -6,19 +6,19 @@
6
7 Requires: fossil.bootstrap, fossil.dom, fossil.popupwidget,
8 fossil.copybutton
9 */
10 var tbl = arg || document.querySelectorAll('table.numbered-lines');
11 if(!tbl) return /* no matching elements */;
12 else if(!arg){
13 if(tbl.length>1){ /* multiple query results: recurse */
14 tbl.forEach( (t)=>callee(t) );
15 return;
16 }else{/* single query result */
17 tbl = tbl[0];
18 }
19 }
 
20 const F = window.fossil, D = F.dom;
21 const tdLn = tbl.querySelector('td.line-numbers');
22 const lineState = {
23 urlArgs: (window.location.search||'?')
24 .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
25
--- src/fossil.numbered-lines.js
+++ src/fossil.numbered-lines.js
@@ -6,19 +6,19 @@
6
7 Requires: fossil.bootstrap, fossil.dom, fossil.popupwidget,
8 fossil.copybutton
9 */
10 var tbl = arg || document.querySelectorAll('table.numbered-lines');
11 if(tbl && !arg){
 
12 if(tbl.length>1){ /* multiple query results: recurse */
13 tbl.forEach( (t)=>callee(t) );
14 return;
15 }else{/* single query result */
16 tbl = tbl[0];
17 }
18 }
19 if(!tbl) return /* no matching elements */;
20 const F = window.fossil, D = F.dom;
21 const tdLn = tbl.querySelector('td.line-numbers');
22 const lineState = {
23 urlArgs: (window.location.search||'?')
24 .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
25
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -464,23 +464,26 @@
464464
const wrapper = D.addClass(
465465
D.attr(D.div(),'id','fileedit-stash-selector'),
466466
'input-with-label'
467467
);
468468
const sel = this.e.select = D.select();
469
- const btnClear = this.e.btnClear
470
- = D.button("Discard Edits");
469
+ const btnClear = this.e.btnClear = D.button("Discard Edits"),
470
+ btnHelp = D.append(
471
+ D.addClass(D.div(), "help-buttonlet"),
472
+ 'Locally-edited files. Timestamps are the last local edit time. ',
473
+ 'Only the ',P.config.defaultMaxStashSize,' most recent files ',
474
+ 'are retained. Saving or reloading a file removes it from this list. ',
475
+ D.append(D.code(),F.storage.storageImplName()),
476
+ ' = ',F.storage.storageHelpDescription()
477
+ );
478
+
471479
D.append(wrapper, "Local edits (",
472480
D.append(D.code(),
473481
F.storage.storageImplName()),
474482
"):",
475
- sel, btnClear);
476
- D.attr(wrapper, "title", [
477
- 'Locally-edited files. Timestamps are the last local edit time.',
478
- 'Only the',P.config.defaultMaxStashSize,'most recent checkin/file',
479
- 'combinations are retained.',
480
- 'Committing or reloading a file removes it from this list.'
481
- ].join(' '));
483
+ btnHelp, sel, btnClear);
484
+ F.helpButtonlets.setup(btnHelp);
482485
D.option(D.disable(sel), "(empty)");
483486
F.page.addEventListener('fileedit-stash-updated',(e)=>this.updateList(e.detail));
484487
F.page.addEventListener('fileedit-file-loaded',(e)=>this.updateList($stash, e.detail));
485488
sel.addEventListener('change',function(e){
486489
const opt = this.selectedOptions[0];
@@ -492,10 +495,11 @@
492495
"Warning: persistent storage is not available, "+
493496
"so uncomitted edits will not survive a page reload."
494497
));
495498
}
496499
domInsertPoint.parentNode.insertBefore(wrapper, domInsertPoint);
500
+ P.tabs.switchToTab(1/*DOM visibility workaround*/);
497501
F.confirmer(btnClear, {
498502
/* must come after insertion into the DOM for the pinSize option to work. */
499503
pinSize: true,
500504
confirmText: "DISCARD all local edits?",
501505
onconfirm: function(e){
@@ -509,10 +513,11 @@
509513
},
510514
ticks: F.config.confirmerButtonTicks
511515
});
512516
D.addClass(this.e.btnClear,'hidden' /* must not be set until after confirmer is set up!*/);
513517
$stash._fireStashEvent(/*read the page-load-time stash*/);
518
+ P.tabs.switchToTab(0/*DOM visibility workaround*/);
514519
delete this.init;
515520
},
516521
/**
517522
Regenerates the edit selection list.
518523
*/
@@ -654,11 +659,11 @@
654659
selectEolWrap: E('#select-eol-style'),
655660
selectEol: E('#select-eol-style select[name=eol]'),
656661
selectFontSizeWrap: E('#select-font-size'),
657662
selectDiffWS: E('select[name=diff_ws]'),
658663
cbLineNumbersWrap: E('#cb-line-numbers'),
659
- cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'),
664
+ cbAutoPreview: E('#cb-preview-autorefresh'),
660665
previewTarget: E('#fileedit-tab-preview-wrapper'),
661666
manifestTarget: E('#fileedit-manifest'),
662667
diffTarget: E('#fileedit-tab-diff-wrapper'),
663668
cbIsExe: E('input[type=checkbox][name=exec_bit]'),
664669
cbManifest: E('input[type=checkbox][name=include_manifest]'),
@@ -737,10 +742,11 @@
737742
"click",(e)=>P.diff(false), false
738743
);
739744
P.e.btnCommit.addEventListener(
740745
"click",(e)=>P.commit(), false
741746
);
747
+ P.tabs.switchToTab(1/*DOM visibility workaround*/);
742748
F.confirmer(P.e.btnReload, {
743749
pinSize: true,
744750
confirmText: "Really reload, losing edits?",
745751
onconfirm: (e)=>P.unstashContent().loadFile(),
746752
ticks: F.config.confirmerButtonTicks
747753
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -464,23 +464,26 @@
464 const wrapper = D.addClass(
465 D.attr(D.div(),'id','fileedit-stash-selector'),
466 'input-with-label'
467 );
468 const sel = this.e.select = D.select();
469 const btnClear = this.e.btnClear
470 = D.button("Discard Edits");
 
 
 
 
 
 
 
 
471 D.append(wrapper, "Local edits (",
472 D.append(D.code(),
473 F.storage.storageImplName()),
474 "):",
475 sel, btnClear);
476 D.attr(wrapper, "title", [
477 'Locally-edited files. Timestamps are the last local edit time.',
478 'Only the',P.config.defaultMaxStashSize,'most recent checkin/file',
479 'combinations are retained.',
480 'Committing or reloading a file removes it from this list.'
481 ].join(' '));
482 D.option(D.disable(sel), "(empty)");
483 F.page.addEventListener('fileedit-stash-updated',(e)=>this.updateList(e.detail));
484 F.page.addEventListener('fileedit-file-loaded',(e)=>this.updateList($stash, e.detail));
485 sel.addEventListener('change',function(e){
486 const opt = this.selectedOptions[0];
@@ -492,10 +495,11 @@
492 "Warning: persistent storage is not available, "+
493 "so uncomitted edits will not survive a page reload."
494 ));
495 }
496 domInsertPoint.parentNode.insertBefore(wrapper, domInsertPoint);
 
497 F.confirmer(btnClear, {
498 /* must come after insertion into the DOM for the pinSize option to work. */
499 pinSize: true,
500 confirmText: "DISCARD all local edits?",
501 onconfirm: function(e){
@@ -509,10 +513,11 @@
509 },
510 ticks: F.config.confirmerButtonTicks
511 });
512 D.addClass(this.e.btnClear,'hidden' /* must not be set until after confirmer is set up!*/);
513 $stash._fireStashEvent(/*read the page-load-time stash*/);
 
514 delete this.init;
515 },
516 /**
517 Regenerates the edit selection list.
518 */
@@ -654,11 +659,11 @@
654 selectEolWrap: E('#select-eol-style'),
655 selectEol: E('#select-eol-style select[name=eol]'),
656 selectFontSizeWrap: E('#select-font-size'),
657 selectDiffWS: E('select[name=diff_ws]'),
658 cbLineNumbersWrap: E('#cb-line-numbers'),
659 cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'),
660 previewTarget: E('#fileedit-tab-preview-wrapper'),
661 manifestTarget: E('#fileedit-manifest'),
662 diffTarget: E('#fileedit-tab-diff-wrapper'),
663 cbIsExe: E('input[type=checkbox][name=exec_bit]'),
664 cbManifest: E('input[type=checkbox][name=include_manifest]'),
@@ -737,10 +742,11 @@
737 "click",(e)=>P.diff(false), false
738 );
739 P.e.btnCommit.addEventListener(
740 "click",(e)=>P.commit(), false
741 );
 
742 F.confirmer(P.e.btnReload, {
743 pinSize: true,
744 confirmText: "Really reload, losing edits?",
745 onconfirm: (e)=>P.unstashContent().loadFile(),
746 ticks: F.config.confirmerButtonTicks
747
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -464,23 +464,26 @@
464 const wrapper = D.addClass(
465 D.attr(D.div(),'id','fileedit-stash-selector'),
466 'input-with-label'
467 );
468 const sel = this.e.select = D.select();
469 const btnClear = this.e.btnClear = D.button("Discard Edits"),
470 btnHelp = D.append(
471 D.addClass(D.div(), "help-buttonlet"),
472 'Locally-edited files. Timestamps are the last local edit time. ',
473 'Only the ',P.config.defaultMaxStashSize,' most recent files ',
474 'are retained. Saving or reloading a file removes it from this list. ',
475 D.append(D.code(),F.storage.storageImplName()),
476 ' = ',F.storage.storageHelpDescription()
477 );
478
479 D.append(wrapper, "Local edits (",
480 D.append(D.code(),
481 F.storage.storageImplName()),
482 "):",
483 btnHelp, sel, btnClear);
484 F.helpButtonlets.setup(btnHelp);
 
 
 
 
 
485 D.option(D.disable(sel), "(empty)");
486 F.page.addEventListener('fileedit-stash-updated',(e)=>this.updateList(e.detail));
487 F.page.addEventListener('fileedit-file-loaded',(e)=>this.updateList($stash, e.detail));
488 sel.addEventListener('change',function(e){
489 const opt = this.selectedOptions[0];
@@ -492,10 +495,11 @@
495 "Warning: persistent storage is not available, "+
496 "so uncomitted edits will not survive a page reload."
497 ));
498 }
499 domInsertPoint.parentNode.insertBefore(wrapper, domInsertPoint);
500 P.tabs.switchToTab(1/*DOM visibility workaround*/);
501 F.confirmer(btnClear, {
502 /* must come after insertion into the DOM for the pinSize option to work. */
503 pinSize: true,
504 confirmText: "DISCARD all local edits?",
505 onconfirm: function(e){
@@ -509,10 +513,11 @@
513 },
514 ticks: F.config.confirmerButtonTicks
515 });
516 D.addClass(this.e.btnClear,'hidden' /* must not be set until after confirmer is set up!*/);
517 $stash._fireStashEvent(/*read the page-load-time stash*/);
518 P.tabs.switchToTab(0/*DOM visibility workaround*/);
519 delete this.init;
520 },
521 /**
522 Regenerates the edit selection list.
523 */
@@ -654,11 +659,11 @@
659 selectEolWrap: E('#select-eol-style'),
660 selectEol: E('#select-eol-style select[name=eol]'),
661 selectFontSizeWrap: E('#select-font-size'),
662 selectDiffWS: E('select[name=diff_ws]'),
663 cbLineNumbersWrap: E('#cb-line-numbers'),
664 cbAutoPreview: E('#cb-preview-autorefresh'),
665 previewTarget: E('#fileedit-tab-preview-wrapper'),
666 manifestTarget: E('#fileedit-manifest'),
667 diffTarget: E('#fileedit-tab-diff-wrapper'),
668 cbIsExe: E('input[type=checkbox][name=exec_bit]'),
669 cbManifest: E('input[type=checkbox][name=include_manifest]'),
@@ -737,10 +742,11 @@
742 "click",(e)=>P.diff(false), false
743 );
744 P.e.btnCommit.addEventListener(
745 "click",(e)=>P.commit(), false
746 );
747 P.tabs.switchToTab(1/*DOM visibility workaround*/);
748 F.confirmer(P.e.btnReload, {
749 pinSize: true,
750 confirmText: "Really reload, losing edits?",
751 onconfirm: (e)=>P.unstashContent().loadFile(),
752 ticks: F.config.confirmerButtonTicks
753
--- src/fossil.page.wikiedit.js
+++ src/fossil.page.wikiedit.js
@@ -2,11 +2,11 @@
22
"use strict";
33
/**
44
Client-side implementation of the /wikiedit app. Requires that
55
the fossil JS bootstrapping is complete and that several fossil
66
JS APIs have been installed: fossil.fetch, fossil.dom,
7
- fossil.tabs, fossil.storage, fossil.confirmer.
7
+ fossil.tabs, fossil.storage, fossil.confirmer, fossil.popupwidget.
88
99
Custom events which can be listened for via
1010
fossil.page.addEventListener():
1111
1212
- Event 'wiki-page-loaded': passes on information when it
@@ -557,11 +557,11 @@
557557
D.attr(sel, 'size', 12);
558558
D.option(D.disable(D.clearElement(sel)), "Loading...");
559559
560560
/** Set up filter checkboxes for the various types
561561
of wiki pages... */
562
- const fsFilter = D.fieldset("Page types"),
562
+ const fsFilter = D.addClass(D.fieldset("Page types"),"page-types-list"),
563563
fsFilterBody = D.div(),
564564
filters = ['normal', 'branch/...', 'tag/...', 'checkin/...']
565565
;
566566
D.append(fsFilter, fsFilterBody);
567567
D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch');
@@ -597,13 +597,18 @@
597597
'deleted'),
598598
'for', cbId),
599599
cb = D.attr(D.input('checkbox'), 'id', cbId);
600600
cb.checked = false;
601601
D.addClass(parentElem,'hide-deleted');
602
- D.attr(lbl, 'title',
603
- 'Fossil considers empty pages to be "deleted" in some contexts.');
604
- D.append(fsFilterBody, D.append(D.span(), cb, lbl));
602
+ D.attr(lbl);
603
+ const deletedTip = F.helpButtonlets.create(
604
+ D.span(),
605
+ 'Fossil considers empty pages to be "deleted" in some contexts.'
606
+ );
607
+ D.append(fsFilterBody, D.append(
608
+ D.span(), cb, lbl, deletedTip
609
+ ));
605610
cb.addEventListener(
606611
'change',
607612
function(ev){
608613
if(ev.target.checked) D.removeClass(parentElem,'hide-deleted');
609614
else D.addClass(parentElem,'hide-deleted');
@@ -682,22 +687,26 @@
682687
init: function(domInsertPoint/*insert widget BEFORE this element*/){
683688
const wrapper = D.addClass(
684689
D.attr(D.div(),'id','wikiedit-stash-selector'),
685690
'input-with-label'
686691
);
687
- const sel = this.e.select = D.select();
688
- const btnClear = this.e.btnClear = D.button("Discard Edits");
692
+ const sel = this.e.select = D.select(),
693
+ btnClear = this.e.btnClear = D.button("Discard Edits"),
694
+ btnHelp = D.append(
695
+ D.addClass(D.div(), "help-buttonlet"),
696
+ 'Locally-edited wiki pages. Timestamps are the last local edit time. ',
697
+ 'Only the ',P.config.defaultMaxStashSize,' most recent pages ',
698
+ 'are retained. Saving or reloading a file removes it from this list. ',
699
+ D.append(D.code(),F.storage.storageImplName()),
700
+ ' = ',F.storage.storageHelpDescription()
701
+ );
689702
D.append(wrapper, "Local edits (",
690703
D.append(D.code(),
691704
F.storage.storageImplName()),
692705
"):",
693
- sel, btnClear);
694
- D.attr(wrapper, "title", [
695
- 'Locally-edited wiki pages. Timestamps are the last local edit time.',
696
- 'Only the',P.config.defaultMaxStashSize,'most recent pages',
697
- 'are retained. Saving or reloading a file removes it from this list.'
698
- ].join(' '));
706
+ btnHelp, sel, btnClear);
707
+ F.helpButtonlets.setup(btnHelp);
699708
D.option(D.disable(sel), "(empty)");
700709
P.addEventListener('wiki-stash-updated',(e)=>this.updateList(e.detail));
701710
P.addEventListener('wiki-page-loaded',(e)=>this.updateList($stash, e.detail));
702711
sel.addEventListener('change',function(e){
703712
const opt = this.selectedOptions[0];
@@ -835,17 +844,15 @@
835844
P.base.originalHref = P.base.tag.href;
836845
P.e = { /* various DOM elements we work with... */
837846
taEditor: E('#wikiedit-content-editor'),
838847
btnReload: E("#wikiedit-tab-content button.wikiedit-content-reload"),
839848
btnSave: E("button.wikiedit-save"),
840
- btnSaveClose: D.attr(E("button.wikiedit-save-close"),
841
- 'title',
842
- 'Save changes and return to the wiki reader.'),
849
+ btnSaveClose: E("button.wikiedit-save-close"),
843850
selectMimetype: E('select[name=mimetype]'),
844851
selectFontSizeWrap: E('#select-font-size'),
845852
// selectDiffWS: E('select[name=diff_ws]'),
846
- cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'),
853
+ cbAutoPreview: E('#cb-preview-autorefresh'),
847854
previewTarget: E('#wikiedit-tab-preview-wrapper'),
848855
diffTarget: E('#wikiedit-tab-diff-wrapper'),
849856
editStatus: E('#wikiedit-edit-status'),
850857
tabContainer: E('#wikiedit-tabs'),
851858
tabs:{
@@ -870,12 +877,12 @@
870877
'before-switch-to', function(ev){
871878
const theTab = ev.detail, btnSlot = theTab.querySelector('.save-button-slot');
872879
if(btnSlot){
873880
/* Several places make sense for a save button, so we'll
874881
move that button around to those tabs where it makes sense. */
875
- btnSlot.parentNode.insertBefore( P.e.btnSave, btnSlot );
876
- btnSlot.parentNode.insertBefore( P.e.btnSaveClose, btnSlot );
882
+ btnSlot.parentNode.insertBefore( P.e.btnSave.parentNode, btnSlot );
883
+ btnSlot.parentNode.insertBefore( P.e.btnSaveClose.parentNode, btnSlot );
877884
P.updateSaveButton();
878885
}
879886
if(theTab===P.e.tabs.preview){
880887
P.baseHrefForWiki();
881888
if(P.previewNeedsUpdate && P.e.cbAutoPreview.checked) P.preview();
@@ -958,10 +965,11 @@
958965
F.message("Discarded new page ["+w.name+"].");
959966
}
960967
};
961968
962969
if(P.config.useConfirmerButtons.reload){
970
+ P.tabs.switchToTab(1/*DOM visibility workaround*/);
963971
F.confirmer(P.e.btnReload, {
964972
pinSize: true,
965973
confirmText: "Really reload, losing edits?",
966974
onconfirm: doReload,
967975
ticks: F.config.confirmerButtonTicks
@@ -968,10 +976,11 @@
968976
});
969977
}else{
970978
P.e.btnReload.addEventListener('click', doReload, false);
971979
}
972980
if(P.config.useConfirmerButtons.save){
981
+ P.tabs.switchToTab(1/*DOM visibility workaround*/);
973982
F.confirmer(P.e.btnSave, {
974983
pinSize: true,
975984
confirmText: "Really save changes?",
976985
onconfirm: ()=>doSave(),
977986
ticks: F.config.confirmerButtonTicks
@@ -1069,13 +1078,19 @@
10691078
}
10701079
P.updatePageTitle().updateSaveButton(/* b/c save() routes through here */);
10711080
},
10721081
false
10731082
);
1074
- /* These init()s need to come after P's event handlers are registered */
1083
+ /* These init()s need to come after P's event handlers are registered.
1084
+ The tab-switching is a workaround for the pinSize option of the confirmer widgets:
1085
+ it does not work if the confirmer button being initialized is in a hidden
1086
+ part of the DOM :/. */
1087
+ P.tabs.switchToTab(0);
10751088
WikiList.init( P.e.tabs.pageList.firstElementChild );
1089
+ P.tabs.switchToTab(1);
10761090
P.stashWidget.init(P.e.tabs.content.lastElementChild);
1091
+ P.tabs.switchToTab(0);
10771092
//P.$wikiList = WikiList/*only for testing/debugging*/;
10781093
}/*F.onPageLoad()*/);
10791094
10801095
/**
10811096
Returns true if fossil.page.winfo is set, indicating that a page
10821097
--- src/fossil.page.wikiedit.js
+++ src/fossil.page.wikiedit.js
@@ -2,11 +2,11 @@
2 "use strict";
3 /**
4 Client-side implementation of the /wikiedit app. Requires that
5 the fossil JS bootstrapping is complete and that several fossil
6 JS APIs have been installed: fossil.fetch, fossil.dom,
7 fossil.tabs, fossil.storage, fossil.confirmer.
8
9 Custom events which can be listened for via
10 fossil.page.addEventListener():
11
12 - Event 'wiki-page-loaded': passes on information when it
@@ -557,11 +557,11 @@
557 D.attr(sel, 'size', 12);
558 D.option(D.disable(D.clearElement(sel)), "Loading...");
559
560 /** Set up filter checkboxes for the various types
561 of wiki pages... */
562 const fsFilter = D.fieldset("Page types"),
563 fsFilterBody = D.div(),
564 filters = ['normal', 'branch/...', 'tag/...', 'checkin/...']
565 ;
566 D.append(fsFilter, fsFilterBody);
567 D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch');
@@ -597,13 +597,18 @@
597 'deleted'),
598 'for', cbId),
599 cb = D.attr(D.input('checkbox'), 'id', cbId);
600 cb.checked = false;
601 D.addClass(parentElem,'hide-deleted');
602 D.attr(lbl, 'title',
603 'Fossil considers empty pages to be "deleted" in some contexts.');
604 D.append(fsFilterBody, D.append(D.span(), cb, lbl));
 
 
 
 
 
605 cb.addEventListener(
606 'change',
607 function(ev){
608 if(ev.target.checked) D.removeClass(parentElem,'hide-deleted');
609 else D.addClass(parentElem,'hide-deleted');
@@ -682,22 +687,26 @@
682 init: function(domInsertPoint/*insert widget BEFORE this element*/){
683 const wrapper = D.addClass(
684 D.attr(D.div(),'id','wikiedit-stash-selector'),
685 'input-with-label'
686 );
687 const sel = this.e.select = D.select();
688 const btnClear = this.e.btnClear = D.button("Discard Edits");
 
 
 
 
 
 
 
 
689 D.append(wrapper, "Local edits (",
690 D.append(D.code(),
691 F.storage.storageImplName()),
692 "):",
693 sel, btnClear);
694 D.attr(wrapper, "title", [
695 'Locally-edited wiki pages. Timestamps are the last local edit time.',
696 'Only the',P.config.defaultMaxStashSize,'most recent pages',
697 'are retained. Saving or reloading a file removes it from this list.'
698 ].join(' '));
699 D.option(D.disable(sel), "(empty)");
700 P.addEventListener('wiki-stash-updated',(e)=>this.updateList(e.detail));
701 P.addEventListener('wiki-page-loaded',(e)=>this.updateList($stash, e.detail));
702 sel.addEventListener('change',function(e){
703 const opt = this.selectedOptions[0];
@@ -835,17 +844,15 @@
835 P.base.originalHref = P.base.tag.href;
836 P.e = { /* various DOM elements we work with... */
837 taEditor: E('#wikiedit-content-editor'),
838 btnReload: E("#wikiedit-tab-content button.wikiedit-content-reload"),
839 btnSave: E("button.wikiedit-save"),
840 btnSaveClose: D.attr(E("button.wikiedit-save-close"),
841 'title',
842 'Save changes and return to the wiki reader.'),
843 selectMimetype: E('select[name=mimetype]'),
844 selectFontSizeWrap: E('#select-font-size'),
845 // selectDiffWS: E('select[name=diff_ws]'),
846 cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'),
847 previewTarget: E('#wikiedit-tab-preview-wrapper'),
848 diffTarget: E('#wikiedit-tab-diff-wrapper'),
849 editStatus: E('#wikiedit-edit-status'),
850 tabContainer: E('#wikiedit-tabs'),
851 tabs:{
@@ -870,12 +877,12 @@
870 'before-switch-to', function(ev){
871 const theTab = ev.detail, btnSlot = theTab.querySelector('.save-button-slot');
872 if(btnSlot){
873 /* Several places make sense for a save button, so we'll
874 move that button around to those tabs where it makes sense. */
875 btnSlot.parentNode.insertBefore( P.e.btnSave, btnSlot );
876 btnSlot.parentNode.insertBefore( P.e.btnSaveClose, btnSlot );
877 P.updateSaveButton();
878 }
879 if(theTab===P.e.tabs.preview){
880 P.baseHrefForWiki();
881 if(P.previewNeedsUpdate && P.e.cbAutoPreview.checked) P.preview();
@@ -958,10 +965,11 @@
958 F.message("Discarded new page ["+w.name+"].");
959 }
960 };
961
962 if(P.config.useConfirmerButtons.reload){
 
963 F.confirmer(P.e.btnReload, {
964 pinSize: true,
965 confirmText: "Really reload, losing edits?",
966 onconfirm: doReload,
967 ticks: F.config.confirmerButtonTicks
@@ -968,10 +976,11 @@
968 });
969 }else{
970 P.e.btnReload.addEventListener('click', doReload, false);
971 }
972 if(P.config.useConfirmerButtons.save){
 
973 F.confirmer(P.e.btnSave, {
974 pinSize: true,
975 confirmText: "Really save changes?",
976 onconfirm: ()=>doSave(),
977 ticks: F.config.confirmerButtonTicks
@@ -1069,13 +1078,19 @@
1069 }
1070 P.updatePageTitle().updateSaveButton(/* b/c save() routes through here */);
1071 },
1072 false
1073 );
1074 /* These init()s need to come after P's event handlers are registered */
 
 
 
 
1075 WikiList.init( P.e.tabs.pageList.firstElementChild );
 
1076 P.stashWidget.init(P.e.tabs.content.lastElementChild);
 
1077 //P.$wikiList = WikiList/*only for testing/debugging*/;
1078 }/*F.onPageLoad()*/);
1079
1080 /**
1081 Returns true if fossil.page.winfo is set, indicating that a page
1082
--- src/fossil.page.wikiedit.js
+++ src/fossil.page.wikiedit.js
@@ -2,11 +2,11 @@
2 "use strict";
3 /**
4 Client-side implementation of the /wikiedit app. Requires that
5 the fossil JS bootstrapping is complete and that several fossil
6 JS APIs have been installed: fossil.fetch, fossil.dom,
7 fossil.tabs, fossil.storage, fossil.confirmer, fossil.popupwidget.
8
9 Custom events which can be listened for via
10 fossil.page.addEventListener():
11
12 - Event 'wiki-page-loaded': passes on information when it
@@ -557,11 +557,11 @@
557 D.attr(sel, 'size', 12);
558 D.option(D.disable(D.clearElement(sel)), "Loading...");
559
560 /** Set up filter checkboxes for the various types
561 of wiki pages... */
562 const fsFilter = D.addClass(D.fieldset("Page types"),"page-types-list"),
563 fsFilterBody = D.div(),
564 filters = ['normal', 'branch/...', 'tag/...', 'checkin/...']
565 ;
566 D.append(fsFilter, fsFilterBody);
567 D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch');
@@ -597,13 +597,18 @@
597 'deleted'),
598 'for', cbId),
599 cb = D.attr(D.input('checkbox'), 'id', cbId);
600 cb.checked = false;
601 D.addClass(parentElem,'hide-deleted');
602 D.attr(lbl);
603 const deletedTip = F.helpButtonlets.create(
604 D.span(),
605 'Fossil considers empty pages to be "deleted" in some contexts.'
606 );
607 D.append(fsFilterBody, D.append(
608 D.span(), cb, lbl, deletedTip
609 ));
610 cb.addEventListener(
611 'change',
612 function(ev){
613 if(ev.target.checked) D.removeClass(parentElem,'hide-deleted');
614 else D.addClass(parentElem,'hide-deleted');
@@ -682,22 +687,26 @@
687 init: function(domInsertPoint/*insert widget BEFORE this element*/){
688 const wrapper = D.addClass(
689 D.attr(D.div(),'id','wikiedit-stash-selector'),
690 'input-with-label'
691 );
692 const sel = this.e.select = D.select(),
693 btnClear = this.e.btnClear = D.button("Discard Edits"),
694 btnHelp = D.append(
695 D.addClass(D.div(), "help-buttonlet"),
696 'Locally-edited wiki pages. Timestamps are the last local edit time. ',
697 'Only the ',P.config.defaultMaxStashSize,' most recent pages ',
698 'are retained. Saving or reloading a file removes it from this list. ',
699 D.append(D.code(),F.storage.storageImplName()),
700 ' = ',F.storage.storageHelpDescription()
701 );
702 D.append(wrapper, "Local edits (",
703 D.append(D.code(),
704 F.storage.storageImplName()),
705 "):",
706 btnHelp, sel, btnClear);
707 F.helpButtonlets.setup(btnHelp);
 
 
 
 
708 D.option(D.disable(sel), "(empty)");
709 P.addEventListener('wiki-stash-updated',(e)=>this.updateList(e.detail));
710 P.addEventListener('wiki-page-loaded',(e)=>this.updateList($stash, e.detail));
711 sel.addEventListener('change',function(e){
712 const opt = this.selectedOptions[0];
@@ -835,17 +844,15 @@
844 P.base.originalHref = P.base.tag.href;
845 P.e = { /* various DOM elements we work with... */
846 taEditor: E('#wikiedit-content-editor'),
847 btnReload: E("#wikiedit-tab-content button.wikiedit-content-reload"),
848 btnSave: E("button.wikiedit-save"),
849 btnSaveClose: E("button.wikiedit-save-close"),
 
 
850 selectMimetype: E('select[name=mimetype]'),
851 selectFontSizeWrap: E('#select-font-size'),
852 // selectDiffWS: E('select[name=diff_ws]'),
853 cbAutoPreview: E('#cb-preview-autorefresh'),
854 previewTarget: E('#wikiedit-tab-preview-wrapper'),
855 diffTarget: E('#wikiedit-tab-diff-wrapper'),
856 editStatus: E('#wikiedit-edit-status'),
857 tabContainer: E('#wikiedit-tabs'),
858 tabs:{
@@ -870,12 +877,12 @@
877 'before-switch-to', function(ev){
878 const theTab = ev.detail, btnSlot = theTab.querySelector('.save-button-slot');
879 if(btnSlot){
880 /* Several places make sense for a save button, so we'll
881 move that button around to those tabs where it makes sense. */
882 btnSlot.parentNode.insertBefore( P.e.btnSave.parentNode, btnSlot );
883 btnSlot.parentNode.insertBefore( P.e.btnSaveClose.parentNode, btnSlot );
884 P.updateSaveButton();
885 }
886 if(theTab===P.e.tabs.preview){
887 P.baseHrefForWiki();
888 if(P.previewNeedsUpdate && P.e.cbAutoPreview.checked) P.preview();
@@ -958,10 +965,11 @@
965 F.message("Discarded new page ["+w.name+"].");
966 }
967 };
968
969 if(P.config.useConfirmerButtons.reload){
970 P.tabs.switchToTab(1/*DOM visibility workaround*/);
971 F.confirmer(P.e.btnReload, {
972 pinSize: true,
973 confirmText: "Really reload, losing edits?",
974 onconfirm: doReload,
975 ticks: F.config.confirmerButtonTicks
@@ -968,10 +976,11 @@
976 });
977 }else{
978 P.e.btnReload.addEventListener('click', doReload, false);
979 }
980 if(P.config.useConfirmerButtons.save){
981 P.tabs.switchToTab(1/*DOM visibility workaround*/);
982 F.confirmer(P.e.btnSave, {
983 pinSize: true,
984 confirmText: "Really save changes?",
985 onconfirm: ()=>doSave(),
986 ticks: F.config.confirmerButtonTicks
@@ -1069,13 +1078,19 @@
1078 }
1079 P.updatePageTitle().updateSaveButton(/* b/c save() routes through here */);
1080 },
1081 false
1082 );
1083 /* These init()s need to come after P's event handlers are registered.
1084 The tab-switching is a workaround for the pinSize option of the confirmer widgets:
1085 it does not work if the confirmer button being initialized is in a hidden
1086 part of the DOM :/. */
1087 P.tabs.switchToTab(0);
1088 WikiList.init( P.e.tabs.pageList.firstElementChild );
1089 P.tabs.switchToTab(1);
1090 P.stashWidget.init(P.e.tabs.content.lastElementChild);
1091 P.tabs.switchToTab(0);
1092 //P.$wikiList = WikiList/*only for testing/debugging*/;
1093 }/*F.onPageLoad()*/);
1094
1095 /**
1096 Returns true if fossil.page.winfo is set, indicating that a page
1097
--- src/fossil.popupwidget.js
+++ src/fossil.popupwidget.js
@@ -264,6 +264,131 @@
264264
error: function(/*...*/){
265265
return toastImpl('error',2,arguments);
266266
}
267267
}/*F.toast*/;
268268
269
+
270
+ F.helpButtonlets = {
271
+ /**
272
+ Initializes one or more "help buttonlets". It may be passed any of:
273
+
274
+ - A string: CSS selector (multiple matches are legal)
275
+
276
+ - A single DOM element.
277
+
278
+ - A forEach-compatible container of DOM elements.
279
+
280
+ - No arguments, which is equivalent to passing the string
281
+ ".help-buttonlet:not(.processed)".
282
+
283
+ Passing the same element(s) more than once is a no-op: during
284
+ initialization, each elements get the class'processed' added to
285
+ it, and any elements with that class are skipped.
286
+
287
+ All child nodes of a help buttonlet are removed from the button
288
+ during initialization and stashed away for use in a PopupWidget
289
+ when the botton is clicked.
290
+
291
+ */
292
+ setup: function f(){
293
+ if(!f.hasOwnProperty('clickHandler')){
294
+ f.clickHandler = function fch(ev){
295
+ if(!fch.popup){
296
+ fch.popup = new F.PopupWidget({
297
+ cssClass: ['fossil-tooltip', 'help-buttonlet-content'],
298
+ refresh: function(){
299
+ }
300
+ });
301
+ fch.popup.e.style.maxWidth = '80%'/*of body*/;
302
+ const hide = ()=>fch.popup.hide();
303
+ fch.popup.e.addEventListener('click', hide, false);
304
+ document.body.addEventListener('click', hide, true);
305
+ document.body.addEventListener('keydown', function(ev){
306
+ if(fch.popup.isShown() && 27===ev.which){
307
+ fch.popup.hide();
308
+ }
309
+ }, true);
310
+ }
311
+ D.append(D.clearElement(fch.popup.e), ev.target.$helpContent);
312
+ var popupRect = ev.target.getClientRects()[0];
313
+ var x = popupRect.left, y = popupRect.top;
314
+ if(x<0) x = 0;
315
+ if(y<0) y = 0;
316
+ /* Shift the help around a bit to "better" fit the
317
+ screen. However, fch.popup.e.getClientRects() is empty
318
+ until the popup is shown, so we have to show it,
319
+ calculate the resulting size, then move and/or resize it.
320
+
321
+ This algorithm/these heuristics can certainly be improved
322
+ upon.
323
+ */
324
+ fch.popup.show(x, y);
325
+ x = popupRect.left, y = popupRect.top;
326
+ popupRect = fch.popup.e.getBoundingClientRect();
327
+ const rectBody = document.body.getClientRects()[0];
328
+ if(popupRect.right > rectBody.right){
329
+ x -= (popupRect.right - rectBody.right);
330
+ }
331
+ if(x + popupRect.width > rectBody.right){
332
+ x = rectBody.x + (rectBody.width*0.1);
333
+ fch.popup.e.style.minWidth = '70%';
334
+ }else{
335
+ fch.popup.e.style.removeProperty('min-width');
336
+ x -= popupRect.width/2;
337
+ }
338
+ if(x<0) x = 0;
339
+ //console.debug("dimensions",x,y, popupRect, rectBody);
340
+ fch.popup.show(x, y);
341
+ };
342
+ f.foreachElement = function(e){
343
+ if(e.classList.contains('processed')) return;
344
+ e.classList.add('processed');
345
+ e.$helpContent = [];
346
+ /* We have to move all child nodes out of the way because we
347
+ cannot hide TEXT nodes via CSS (which cannot select TEXT
348
+ nodes). We have to do it in two steps to avoid invaliding
349
+ the list during traversal. */
350
+ e.childNodes.forEach((ch)=>e.$helpContent.push(ch));
351
+ e.$helpContent.forEach((ch)=>ch.remove());
352
+ e.addEventListener('click', f.clickHandler, false);
353
+ };
354
+ }/*static init*/
355
+ var elems;
356
+ if(!arguments.length){
357
+ arguments[0] = '.help-buttonlet:not(.processed)';
358
+ arguments.length = 1;
359
+ }
360
+ if(arguments.length){
361
+ if('string'===typeof arguments[0]){
362
+ elems = document.querySelectorAll(arguments[0]);
363
+ }else if(arguments[0] instanceof HTMLElement){
364
+ elems = [arguments[0]];
365
+ }else if(arguments[0].forEach){/* assume DOM element list or array */
366
+ elems = arguments[0];
367
+ }
368
+ }
369
+ if(elems) elems.forEach(f.foreachElement);
370
+ },
371
+
372
+ /**
373
+ Sets up the given element as a "help buttonlet", adding the CSS
374
+ class help-buttonlet to it. Any (optional) arguments after the
375
+ first are appended to the element using fossil.dom.append(), so
376
+ that they become the content for the buttonlet's popup help.
377
+
378
+ The element is then passed to this.setup() before it
379
+ is returned from this function.
380
+ */
381
+ create: function(elem/*...body*/){
382
+ D.addClass(elem, 'help-buttonlet');
383
+ if(arguments.length>1){
384
+ const args = Array.prototype.slice.call(arguments,1);
385
+ D.append(elem, args);
386
+ }
387
+ this.setup(elem);
388
+ return elem;
389
+ }
390
+ }/*helpButtonlets*/;
391
+
392
+ F.onDOMContentLoaded( ()=>F.helpButtonlets.setup() );
393
+
269394
})(window.fossil);
270395
--- src/fossil.popupwidget.js
+++ src/fossil.popupwidget.js
@@ -264,6 +264,131 @@
264 error: function(/*...*/){
265 return toastImpl('error',2,arguments);
266 }
267 }/*F.toast*/;
268
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269 })(window.fossil);
270
--- src/fossil.popupwidget.js
+++ src/fossil.popupwidget.js
@@ -264,6 +264,131 @@
264 error: function(/*...*/){
265 return toastImpl('error',2,arguments);
266 }
267 }/*F.toast*/;
268
269
270 F.helpButtonlets = {
271 /**
272 Initializes one or more "help buttonlets". It may be passed any of:
273
274 - A string: CSS selector (multiple matches are legal)
275
276 - A single DOM element.
277
278 - A forEach-compatible container of DOM elements.
279
280 - No arguments, which is equivalent to passing the string
281 ".help-buttonlet:not(.processed)".
282
283 Passing the same element(s) more than once is a no-op: during
284 initialization, each elements get the class'processed' added to
285 it, and any elements with that class are skipped.
286
287 All child nodes of a help buttonlet are removed from the button
288 during initialization and stashed away for use in a PopupWidget
289 when the botton is clicked.
290
291 */
292 setup: function f(){
293 if(!f.hasOwnProperty('clickHandler')){
294 f.clickHandler = function fch(ev){
295 if(!fch.popup){
296 fch.popup = new F.PopupWidget({
297 cssClass: ['fossil-tooltip', 'help-buttonlet-content'],
298 refresh: function(){
299 }
300 });
301 fch.popup.e.style.maxWidth = '80%'/*of body*/;
302 const hide = ()=>fch.popup.hide();
303 fch.popup.e.addEventListener('click', hide, false);
304 document.body.addEventListener('click', hide, true);
305 document.body.addEventListener('keydown', function(ev){
306 if(fch.popup.isShown() && 27===ev.which){
307 fch.popup.hide();
308 }
309 }, true);
310 }
311 D.append(D.clearElement(fch.popup.e), ev.target.$helpContent);
312 var popupRect = ev.target.getClientRects()[0];
313 var x = popupRect.left, y = popupRect.top;
314 if(x<0) x = 0;
315 if(y<0) y = 0;
316 /* Shift the help around a bit to "better" fit the
317 screen. However, fch.popup.e.getClientRects() is empty
318 until the popup is shown, so we have to show it,
319 calculate the resulting size, then move and/or resize it.
320
321 This algorithm/these heuristics can certainly be improved
322 upon.
323 */
324 fch.popup.show(x, y);
325 x = popupRect.left, y = popupRect.top;
326 popupRect = fch.popup.e.getBoundingClientRect();
327 const rectBody = document.body.getClientRects()[0];
328 if(popupRect.right > rectBody.right){
329 x -= (popupRect.right - rectBody.right);
330 }
331 if(x + popupRect.width > rectBody.right){
332 x = rectBody.x + (rectBody.width*0.1);
333 fch.popup.e.style.minWidth = '70%';
334 }else{
335 fch.popup.e.style.removeProperty('min-width');
336 x -= popupRect.width/2;
337 }
338 if(x<0) x = 0;
339 //console.debug("dimensions",x,y, popupRect, rectBody);
340 fch.popup.show(x, y);
341 };
342 f.foreachElement = function(e){
343 if(e.classList.contains('processed')) return;
344 e.classList.add('processed');
345 e.$helpContent = [];
346 /* We have to move all child nodes out of the way because we
347 cannot hide TEXT nodes via CSS (which cannot select TEXT
348 nodes). We have to do it in two steps to avoid invaliding
349 the list during traversal. */
350 e.childNodes.forEach((ch)=>e.$helpContent.push(ch));
351 e.$helpContent.forEach((ch)=>ch.remove());
352 e.addEventListener('click', f.clickHandler, false);
353 };
354 }/*static init*/
355 var elems;
356 if(!arguments.length){
357 arguments[0] = '.help-buttonlet:not(.processed)';
358 arguments.length = 1;
359 }
360 if(arguments.length){
361 if('string'===typeof arguments[0]){
362 elems = document.querySelectorAll(arguments[0]);
363 }else if(arguments[0] instanceof HTMLElement){
364 elems = [arguments[0]];
365 }else if(arguments[0].forEach){/* assume DOM element list or array */
366 elems = arguments[0];
367 }
368 }
369 if(elems) elems.forEach(f.foreachElement);
370 },
371
372 /**
373 Sets up the given element as a "help buttonlet", adding the CSS
374 class help-buttonlet to it. Any (optional) arguments after the
375 first are appended to the element using fossil.dom.append(), so
376 that they become the content for the buttonlet's popup help.
377
378 The element is then passed to this.setup() before it
379 is returned from this function.
380 */
381 create: function(elem/*...body*/){
382 D.addClass(elem, 'help-buttonlet');
383 if(arguments.length>1){
384 const args = Array.prototype.slice.call(arguments,1);
385 D.append(elem, args);
386 }
387 this.setup(elem);
388 return elem;
389 }
390 }/*helpButtonlets*/;
391
392 F.onDOMContentLoaded( ()=>F.helpButtonlets.setup() );
393
394 })(window.fossil);
395
--- src/fossil.storage.js
+++ src/fossil.storage.js
@@ -135,9 +135,24 @@
135135
/** Returns a symbolic name for the current storage mechanism. */
136136
storageImplName: function(){
137137
if($storage===window.localStorage) return 'localStorage';
138138
else if($storage===window.sessionStorage) return 'sessionStorage';
139139
else return 'transient';
140
+ },
141
+
142
+ /**
143
+ Returns a brief help text string for the currently-selected
144
+ storage type.
145
+ */
146
+ storageHelpDescription: function(){
147
+ return {
148
+ localStorage: "Browser-local persistent storage with an "+
149
+ "unspecified long-term lifetime (survives closing the browser, "+
150
+ "but maybe not a browser upgrade).",
151
+ sessionStorage: "Storage local to this browser tab, "+
152
+ "lost if this tab is closed.",
153
+ "transient": "Transient storage local to this invocation of this page."
154
+ }[this.storageImplName()];
140155
}
141156
};
142157
143158
})(window.fossil);
144159
--- src/fossil.storage.js
+++ src/fossil.storage.js
@@ -135,9 +135,24 @@
135 /** Returns a symbolic name for the current storage mechanism. */
136 storageImplName: function(){
137 if($storage===window.localStorage) return 'localStorage';
138 else if($storage===window.sessionStorage) return 'sessionStorage';
139 else return 'transient';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140 }
141 };
142
143 })(window.fossil);
144
--- src/fossil.storage.js
+++ src/fossil.storage.js
@@ -135,9 +135,24 @@
135 /** Returns a symbolic name for the current storage mechanism. */
136 storageImplName: function(){
137 if($storage===window.localStorage) return 'localStorage';
138 else if($storage===window.sessionStorage) return 'sessionStorage';
139 else return 'transient';
140 },
141
142 /**
143 Returns a brief help text string for the currently-selected
144 storage type.
145 */
146 storageHelpDescription: function(){
147 return {
148 localStorage: "Browser-local persistent storage with an "+
149 "unspecified long-term lifetime (survives closing the browser, "+
150 "but maybe not a browser upgrade).",
151 sessionStorage: "Storage local to this browser tab, "+
152 "lost if this tab is closed.",
153 "transient": "Transient storage local to this invocation of this page."
154 }[this.storageImplName()];
155 }
156 };
157
158 })(window.fossil);
159
+21 -7
--- src/info.c
+++ src/info.c
@@ -2024,24 +2024,33 @@
20242024
** zLn is the ?ln= parameter for the HTTP query. If there is an argument,
20252025
** then highlight that line number and scroll to it once the page loads.
20262026
** If there are two line numbers, highlight the range of lines.
20272027
** Multiple ranges can be highlighed by adding additional line numbers
20282028
** separated by a non-digit character (also not one of [-,.]).
2029
+**
2030
+** If includeJS is true then the JS code associated with line
2031
+** numbering is also emitted, else it is not. If this routine is
2032
+** called multiple times in a single app run, the JS is emitted only
2033
+** once. Note that when using this routine to emit Ajax responses, the
2034
+** JS should be not be included, as it will not get imported properly
2035
+** into the response's rendering.
20292036
*/
20302037
void output_text_with_line_numbers(
20312038
const char *z,
20322039
int nZ,
20332040
const char *zName,
2034
- const char *zLn
2041
+ const char *zLn,
2042
+ int includeJS
20352043
){
20362044
int iStart, iEnd; /* Start and end of region to highlight */
20372045
int n = 0; /* Current line number */
20382046
int i = 0; /* Loop index */
20392047
int iTop = 0; /* Scroll so that this line is on top of screen. */
20402048
int nLine = 0; /* content line count */
20412049
int nSpans = 0; /* number of distinct zLn spans */
20422050
const char *zExt = file_extension(zName);
2051
+ static int emittedJS = 0; /* emitted shared JS yet? */
20432052
Stmt q;
20442053
20452054
iStart = iEnd = atoi(zLn);
20462055
db_multi_exec(
20472056
"CREATE TEMP TABLE lnos(iStart INTEGER PRIMARY KEY, iEnd INTEGER)");
@@ -2115,15 +2124,20 @@
21152124
}else{
21162125
cgi_append_content("<code>", -1);
21172126
}
21182127
cgi_printf("%z", htmlize(z, nZ));
21192128
CX("</code></pre></td></tr></tbody></table>\n");
2120
- if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){
2121
- builtin_request_js("scroll.js");
2129
+ if(includeJS && !emittedJS){
2130
+ emittedJS = 1;
2131
+ if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){
2132
+ builtin_request_js("scroll.js");
2133
+ }
2134
+ if(!builtin_bundle_all_fossil_js_apis()){
2135
+ builtin_emit_fossil_js_apis("dom", "copybutton", "popupwidget",
2136
+ "numbered-lines", 0);
2137
+ }
21222138
}
2123
- style_emit_fossil_js_apis(0, "dom", "copybutton", "popupwidget",
2124
- "numbered-lines", 0);
21252139
}
21262140
21272141
/*
21282142
** COMMAND: test-line-numbers
21292143
**
@@ -2144,11 +2158,11 @@
21442158
zFilename = g.argv[2];
21452159
fossil_print("%s %s\n", zFilename, zLn);
21462160
21472161
blob_read_from_file(&content, zFilename, ExtFILE);
21482162
output_text_with_line_numbers(blob_str(&content), blob_size(&content),
2149
- zFilename, zLn);
2163
+ zFilename, zLn, 0);
21502164
blob_reset(&content);
21512165
fossil_print("%b\n", cgi_output_blob());
21522166
}
21532167
21542168
/*
@@ -2456,11 +2470,11 @@
24562470
" AND mlink.fid=%d",
24572471
rid);
24582472
zExt = zFileName ? file_extension(zFileName) : 0;
24592473
if( zLn ){
24602474
output_text_with_line_numbers(z, blob_size(&content),
2461
- zFileName, zLn);
2475
+ zFileName, zLn, 1);
24622476
}else if( zExt && zExt[1] ){
24632477
@ <pre>
24642478
@ <code class="language-%s(zExt)">%h(z)</code>
24652479
@ </pre>
24662480
}else{
24672481
--- src/info.c
+++ src/info.c
@@ -2024,24 +2024,33 @@
2024 ** zLn is the ?ln= parameter for the HTTP query. If there is an argument,
2025 ** then highlight that line number and scroll to it once the page loads.
2026 ** If there are two line numbers, highlight the range of lines.
2027 ** Multiple ranges can be highlighed by adding additional line numbers
2028 ** separated by a non-digit character (also not one of [-,.]).
 
 
 
 
 
 
 
2029 */
2030 void output_text_with_line_numbers(
2031 const char *z,
2032 int nZ,
2033 const char *zName,
2034 const char *zLn
 
2035 ){
2036 int iStart, iEnd; /* Start and end of region to highlight */
2037 int n = 0; /* Current line number */
2038 int i = 0; /* Loop index */
2039 int iTop = 0; /* Scroll so that this line is on top of screen. */
2040 int nLine = 0; /* content line count */
2041 int nSpans = 0; /* number of distinct zLn spans */
2042 const char *zExt = file_extension(zName);
 
2043 Stmt q;
2044
2045 iStart = iEnd = atoi(zLn);
2046 db_multi_exec(
2047 "CREATE TEMP TABLE lnos(iStart INTEGER PRIMARY KEY, iEnd INTEGER)");
@@ -2115,15 +2124,20 @@
2115 }else{
2116 cgi_append_content("<code>", -1);
2117 }
2118 cgi_printf("%z", htmlize(z, nZ));
2119 CX("</code></pre></td></tr></tbody></table>\n");
2120 if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){
2121 builtin_request_js("scroll.js");
 
 
 
 
 
 
 
2122 }
2123 style_emit_fossil_js_apis(0, "dom", "copybutton", "popupwidget",
2124 "numbered-lines", 0);
2125 }
2126
2127 /*
2128 ** COMMAND: test-line-numbers
2129 **
@@ -2144,11 +2158,11 @@
2144 zFilename = g.argv[2];
2145 fossil_print("%s %s\n", zFilename, zLn);
2146
2147 blob_read_from_file(&content, zFilename, ExtFILE);
2148 output_text_with_line_numbers(blob_str(&content), blob_size(&content),
2149 zFilename, zLn);
2150 blob_reset(&content);
2151 fossil_print("%b\n", cgi_output_blob());
2152 }
2153
2154 /*
@@ -2456,11 +2470,11 @@
2456 " AND mlink.fid=%d",
2457 rid);
2458 zExt = zFileName ? file_extension(zFileName) : 0;
2459 if( zLn ){
2460 output_text_with_line_numbers(z, blob_size(&content),
2461 zFileName, zLn);
2462 }else if( zExt && zExt[1] ){
2463 @ <pre>
2464 @ <code class="language-%s(zExt)">%h(z)</code>
2465 @ </pre>
2466 }else{
2467
--- src/info.c
+++ src/info.c
@@ -2024,24 +2024,33 @@
2024 ** zLn is the ?ln= parameter for the HTTP query. If there is an argument,
2025 ** then highlight that line number and scroll to it once the page loads.
2026 ** If there are two line numbers, highlight the range of lines.
2027 ** Multiple ranges can be highlighed by adding additional line numbers
2028 ** separated by a non-digit character (also not one of [-,.]).
2029 **
2030 ** If includeJS is true then the JS code associated with line
2031 ** numbering is also emitted, else it is not. If this routine is
2032 ** called multiple times in a single app run, the JS is emitted only
2033 ** once. Note that when using this routine to emit Ajax responses, the
2034 ** JS should be not be included, as it will not get imported properly
2035 ** into the response's rendering.
2036 */
2037 void output_text_with_line_numbers(
2038 const char *z,
2039 int nZ,
2040 const char *zName,
2041 const char *zLn,
2042 int includeJS
2043 ){
2044 int iStart, iEnd; /* Start and end of region to highlight */
2045 int n = 0; /* Current line number */
2046 int i = 0; /* Loop index */
2047 int iTop = 0; /* Scroll so that this line is on top of screen. */
2048 int nLine = 0; /* content line count */
2049 int nSpans = 0; /* number of distinct zLn spans */
2050 const char *zExt = file_extension(zName);
2051 static int emittedJS = 0; /* emitted shared JS yet? */
2052 Stmt q;
2053
2054 iStart = iEnd = atoi(zLn);
2055 db_multi_exec(
2056 "CREATE TEMP TABLE lnos(iStart INTEGER PRIMARY KEY, iEnd INTEGER)");
@@ -2115,15 +2124,20 @@
2124 }else{
2125 cgi_append_content("<code>", -1);
2126 }
2127 cgi_printf("%z", htmlize(z, nZ));
2128 CX("</code></pre></td></tr></tbody></table>\n");
2129 if(includeJS && !emittedJS){
2130 emittedJS = 1;
2131 if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){
2132 builtin_request_js("scroll.js");
2133 }
2134 if(!builtin_bundle_all_fossil_js_apis()){
2135 builtin_emit_fossil_js_apis("dom", "copybutton", "popupwidget",
2136 "numbered-lines", 0);
2137 }
2138 }
 
 
2139 }
2140
2141 /*
2142 ** COMMAND: test-line-numbers
2143 **
@@ -2144,11 +2158,11 @@
2158 zFilename = g.argv[2];
2159 fossil_print("%s %s\n", zFilename, zLn);
2160
2161 blob_read_from_file(&content, zFilename, ExtFILE);
2162 output_text_with_line_numbers(blob_str(&content), blob_size(&content),
2163 zFilename, zLn, 0);
2164 blob_reset(&content);
2165 fossil_print("%b\n", cgi_output_blob());
2166 }
2167
2168 /*
@@ -2456,11 +2470,11 @@
2470 " AND mlink.fid=%d",
2471 rid);
2472 zExt = zFileName ? file_extension(zFileName) : 0;
2473 if( zLn ){
2474 output_text_with_line_numbers(z, blob_size(&content),
2475 zFileName, zLn, 1);
2476 }else if( zExt && zExt[1] ){
2477 @ <pre>
2478 @ <code class="language-%s(zExt)">%h(z)</code>
2479 @ </pre>
2480 }else{
2481
+1 -1
--- src/lookslike.c
+++ src/lookslike.c
@@ -400,11 +400,11 @@
400400
** COMMAND: test-looks-like-utf
401401
**
402402
** Usage: %fossil test-looks-like-utf FILENAME
403403
**
404404
** Options:
405
-** -n|--limit <num> Repeat looks-like function <num> times, for
405
+** -n|--limit N Repeat looks-like function N times, for
406406
** performance measurement. Default = 1;
407407
** --utf8 Ignoring BOM and file size, force UTF-8 checking
408408
** --utf16 Ignoring BOM and file size, force UTF-16 checking
409409
**
410410
** FILENAME is the name of a file to check for textual content in the UTF-8
411411
--- src/lookslike.c
+++ src/lookslike.c
@@ -400,11 +400,11 @@
400 ** COMMAND: test-looks-like-utf
401 **
402 ** Usage: %fossil test-looks-like-utf FILENAME
403 **
404 ** Options:
405 ** -n|--limit <num> Repeat looks-like function <num> times, for
406 ** performance measurement. Default = 1;
407 ** --utf8 Ignoring BOM and file size, force UTF-8 checking
408 ** --utf16 Ignoring BOM and file size, force UTF-16 checking
409 **
410 ** FILENAME is the name of a file to check for textual content in the UTF-8
411
--- src/lookslike.c
+++ src/lookslike.c
@@ -400,11 +400,11 @@
400 ** COMMAND: test-looks-like-utf
401 **
402 ** Usage: %fossil test-looks-like-utf FILENAME
403 **
404 ** Options:
405 ** -n|--limit N Repeat looks-like function N times, for
406 ** performance measurement. Default = 1;
407 ** --utf8 Ignoring BOM and file size, force UTF-8 checking
408 ** --utf16 Ignoring BOM and file size, force UTF-16 checking
409 **
410 ** FILENAME is the name of a file to check for textual content in the UTF-8
411
+63 -6
--- src/merge3.c
+++ src/merge3.c
@@ -138,16 +138,47 @@
138138
/*
139139
** Text of boundary markers for merge conflicts.
140140
*/
141141
static const char *const mergeMarker[] = {
142142
/*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/
143
- "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n",
144
- "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||\n",
145
- "======= MERGED IN content follows ==================================\n",
146
- ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
143
+ "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<",
144
+ "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||",
145
+ "======= MERGED IN content follows ==================================",
146
+ ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
147147
};
148148
149
+/*
150
+** Return true if the input blob contains any CR/LF pairs on the first
151
+** ten lines. This should be enough to detect files that use mainly CR/LF
152
+** line endings without causing a performance impact for LF only files.
153
+*/
154
+int contains_crlf(Blob *p){
155
+ int i;
156
+ int j = 0;
157
+ const int maxL = 10; /* Max lines to check */
158
+ const char *z = blob_buffer(p);
159
+ int n = blob_size(p)+1;
160
+ for(i=1; i<n; ){
161
+ if( z[i-1]=='\r' && z[i]=='\n' ) return 1;
162
+ while( i<n && z[i]!='\n' ){ i++; }
163
+ j++;
164
+ if( j>maxL ) return 0;
165
+ }
166
+ return 0;
167
+}
168
+
169
+/*
170
+** Ensure that the text in pBlob ends with a new line.
171
+** If useCrLf is true adds "\r\n" otherwise '\n'.
172
+*/
173
+void ensure_line_end(Blob *pBlob, int useCrLf){
174
+ if( pBlob->nUsed<=0 ) return;
175
+ if( pBlob->aData[pBlob->nUsed-1]!='\n' ){
176
+ if( useCrLf ) blob_append_char(pBlob, '\r');
177
+ blob_append_char(pBlob, '\n');
178
+ }
179
+}
149180
150181
/*
151182
** Do a three-way merge. Initialize pOut to contain the result.
152183
**
153184
** The merge is an edit against pV2. Both pV1 and pV2 have a
@@ -166,10 +197,28 @@
166197
int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */
167198
int limit1, limit2; /* Sizes of aC1[] and aC2[] */
168199
int nConflict = 0; /* Number of merge conflicts seen so far */
169200
170201
blob_zero(pOut); /* Merge results stored in pOut */
202
+
203
+ /* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM),
204
+ ** keep it in the output. This should be secure enough not to cause
205
+ ** unintended changes to the merged file and consistent with what
206
+ ** users are using in their source files.
207
+ */
208
+ if( starts_with_utf8_bom(pV1, 0) && starts_with_utf8_bom(pV2, 0) ){
209
+ blob_append(pOut, (char*)get_utf8_bom(0), -1);
210
+ }
211
+
212
+ /* Check once to see if both pV1 and pV2 contains CR/LF endings.
213
+ ** If true, CR/LF pair will be used later to append the
214
+ ** boundary markers for merge conflicts.
215
+ */
216
+ int useCrLf = 0;
217
+ if( contains_crlf(pV1) && contains_crlf(pV2) ){
218
+ useCrLf = 1;
219
+ }
171220
172221
/* Compute the edits that occur from pPivot => pV1 (into aC1)
173222
** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is
174223
** an array of integer triples. Within each triple, the first integer
175224
** is the number of lines of text to copy directly from the pivot,
@@ -269,16 +318,23 @@
269318
while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){
270319
sz++;
271320
}
272321
DEBUG( printf("CONFLICT %d\n", sz); )
273322
blob_append(pOut, mergeMarker[0], -1);
323
+ ensure_line_end(pOut, useCrLf);
274324
i1 = output_one_side(pOut, pV1, aC1, i1, sz);
325
+ ensure_line_end(pOut, useCrLf);
275326
blob_append(pOut, mergeMarker[1], -1);
327
+ ensure_line_end(pOut, useCrLf);
276328
blob_copy_lines(pOut, pPivot, sz);
329
+ ensure_line_end(pOut, useCrLf);
277330
blob_append(pOut, mergeMarker[2], -1);
331
+ ensure_line_end(pOut, useCrLf);
278332
i2 = output_one_side(pOut, pV2, aC2, i2, sz);
333
+ ensure_line_end(pOut, useCrLf);
279334
blob_append(pOut, mergeMarker[3], -1);
335
+ ensure_line_end(pOut, useCrLf);
280336
}
281337
282338
/* If we are finished with an edit triple, advance to the next
283339
** triple.
284340
*/
@@ -319,14 +375,15 @@
319375
assert( len==(int)strlen(mergeMarker[2]) );
320376
assert( len==(int)strlen(mergeMarker[3]) );
321377
assert( count(mergeMarker)==4 );
322378
for(i=0; i<n; ){
323379
for(j=0; j<4; j++){
324
- if( memcmp(&z[i], mergeMarker[j], len)==0 ) return 1;
380
+ if( (memcmp(&z[i], mergeMarker[j], len)==0)
381
+ && (i+1==n || z[i+len]=='\n' || z[i+len]=='\r') ) return 1;
325382
}
326383
while( i<n && z[i]!='\n' ){ i++; }
327
- while( i<n && z[i]=='\n' ){ i++; }
384
+ while( i<n && (z[i]=='\n' || z[i]=='\r') ){ i++; }
328385
}
329386
return 0;
330387
}
331388
332389
/*
333390
--- src/merge3.c
+++ src/merge3.c
@@ -138,16 +138,47 @@
138 /*
139 ** Text of boundary markers for merge conflicts.
140 */
141 static const char *const mergeMarker[] = {
142 /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/
143 "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n",
144 "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||\n",
145 "======= MERGED IN content follows ==================================\n",
146 ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
147 };
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
150 /*
151 ** Do a three-way merge. Initialize pOut to contain the result.
152 **
153 ** The merge is an edit against pV2. Both pV1 and pV2 have a
@@ -166,10 +197,28 @@
166 int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */
167 int limit1, limit2; /* Sizes of aC1[] and aC2[] */
168 int nConflict = 0; /* Number of merge conflicts seen so far */
169
170 blob_zero(pOut); /* Merge results stored in pOut */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
172 /* Compute the edits that occur from pPivot => pV1 (into aC1)
173 ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is
174 ** an array of integer triples. Within each triple, the first integer
175 ** is the number of lines of text to copy directly from the pivot,
@@ -269,16 +318,23 @@
269 while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){
270 sz++;
271 }
272 DEBUG( printf("CONFLICT %d\n", sz); )
273 blob_append(pOut, mergeMarker[0], -1);
 
274 i1 = output_one_side(pOut, pV1, aC1, i1, sz);
 
275 blob_append(pOut, mergeMarker[1], -1);
 
276 blob_copy_lines(pOut, pPivot, sz);
 
277 blob_append(pOut, mergeMarker[2], -1);
 
278 i2 = output_one_side(pOut, pV2, aC2, i2, sz);
 
279 blob_append(pOut, mergeMarker[3], -1);
 
280 }
281
282 /* If we are finished with an edit triple, advance to the next
283 ** triple.
284 */
@@ -319,14 +375,15 @@
319 assert( len==(int)strlen(mergeMarker[2]) );
320 assert( len==(int)strlen(mergeMarker[3]) );
321 assert( count(mergeMarker)==4 );
322 for(i=0; i<n; ){
323 for(j=0; j<4; j++){
324 if( memcmp(&z[i], mergeMarker[j], len)==0 ) return 1;
 
325 }
326 while( i<n && z[i]!='\n' ){ i++; }
327 while( i<n && z[i]=='\n' ){ i++; }
328 }
329 return 0;
330 }
331
332 /*
333
--- src/merge3.c
+++ src/merge3.c
@@ -138,16 +138,47 @@
138 /*
139 ** Text of boundary markers for merge conflicts.
140 */
141 static const char *const mergeMarker[] = {
142 /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/
143 "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<",
144 "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||",
145 "======= MERGED IN content follows ==================================",
146 ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
147 };
148
149 /*
150 ** Return true if the input blob contains any CR/LF pairs on the first
151 ** ten lines. This should be enough to detect files that use mainly CR/LF
152 ** line endings without causing a performance impact for LF only files.
153 */
154 int contains_crlf(Blob *p){
155 int i;
156 int j = 0;
157 const int maxL = 10; /* Max lines to check */
158 const char *z = blob_buffer(p);
159 int n = blob_size(p)+1;
160 for(i=1; i<n; ){
161 if( z[i-1]=='\r' && z[i]=='\n' ) return 1;
162 while( i<n && z[i]!='\n' ){ i++; }
163 j++;
164 if( j>maxL ) return 0;
165 }
166 return 0;
167 }
168
169 /*
170 ** Ensure that the text in pBlob ends with a new line.
171 ** If useCrLf is true adds "\r\n" otherwise '\n'.
172 */
173 void ensure_line_end(Blob *pBlob, int useCrLf){
174 if( pBlob->nUsed<=0 ) return;
175 if( pBlob->aData[pBlob->nUsed-1]!='\n' ){
176 if( useCrLf ) blob_append_char(pBlob, '\r');
177 blob_append_char(pBlob, '\n');
178 }
179 }
180
181 /*
182 ** Do a three-way merge. Initialize pOut to contain the result.
183 **
184 ** The merge is an edit against pV2. Both pV1 and pV2 have a
@@ -166,10 +197,28 @@
197 int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */
198 int limit1, limit2; /* Sizes of aC1[] and aC2[] */
199 int nConflict = 0; /* Number of merge conflicts seen so far */
200
201 blob_zero(pOut); /* Merge results stored in pOut */
202
203 /* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM),
204 ** keep it in the output. This should be secure enough not to cause
205 ** unintended changes to the merged file and consistent with what
206 ** users are using in their source files.
207 */
208 if( starts_with_utf8_bom(pV1, 0) && starts_with_utf8_bom(pV2, 0) ){
209 blob_append(pOut, (char*)get_utf8_bom(0), -1);
210 }
211
212 /* Check once to see if both pV1 and pV2 contains CR/LF endings.
213 ** If true, CR/LF pair will be used later to append the
214 ** boundary markers for merge conflicts.
215 */
216 int useCrLf = 0;
217 if( contains_crlf(pV1) && contains_crlf(pV2) ){
218 useCrLf = 1;
219 }
220
221 /* Compute the edits that occur from pPivot => pV1 (into aC1)
222 ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is
223 ** an array of integer triples. Within each triple, the first integer
224 ** is the number of lines of text to copy directly from the pivot,
@@ -269,16 +318,23 @@
318 while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){
319 sz++;
320 }
321 DEBUG( printf("CONFLICT %d\n", sz); )
322 blob_append(pOut, mergeMarker[0], -1);
323 ensure_line_end(pOut, useCrLf);
324 i1 = output_one_side(pOut, pV1, aC1, i1, sz);
325 ensure_line_end(pOut, useCrLf);
326 blob_append(pOut, mergeMarker[1], -1);
327 ensure_line_end(pOut, useCrLf);
328 blob_copy_lines(pOut, pPivot, sz);
329 ensure_line_end(pOut, useCrLf);
330 blob_append(pOut, mergeMarker[2], -1);
331 ensure_line_end(pOut, useCrLf);
332 i2 = output_one_side(pOut, pV2, aC2, i2, sz);
333 ensure_line_end(pOut, useCrLf);
334 blob_append(pOut, mergeMarker[3], -1);
335 ensure_line_end(pOut, useCrLf);
336 }
337
338 /* If we are finished with an edit triple, advance to the next
339 ** triple.
340 */
@@ -319,14 +375,15 @@
375 assert( len==(int)strlen(mergeMarker[2]) );
376 assert( len==(int)strlen(mergeMarker[3]) );
377 assert( count(mergeMarker)==4 );
378 for(i=0; i<n; ){
379 for(j=0; j<4; j++){
380 if( (memcmp(&z[i], mergeMarker[j], len)==0)
381 && (i+1==n || z[i+len]=='\n' || z[i+len]=='\r') ) return 1;
382 }
383 while( i<n && z[i]!='\n' ){ i++; }
384 while( i<n && (z[i]=='\n' || z[i]=='\r') ){ i++; }
385 }
386 return 0;
387 }
388
389 /*
390
+1 -1
--- src/stash.c
+++ src/stash.c
@@ -523,11 +523,11 @@
523523
** are listed, then only stash and revert the named files. The
524524
** "save" verb can be omitted if and only if there are no other
525525
** arguments. The "snapshot" verb works the same as "save" but
526526
** omits the revert, keeping the checkout unchanged.
527527
**
528
-** > fossil stash list|ls ?-v|--verbose? ?-W|--width <num>?
528
+** > fossil stash list|ls ?-v|--verbose? ?-W|--width NUM?
529529
**
530530
** List all changes sets currently stashed. Show information about
531531
** individual files in each changeset if -v or --verbose is used.
532532
**
533533
** > fossil stash show|cat ?STASHID? ?DIFF-OPTIONS?
534534
--- src/stash.c
+++ src/stash.c
@@ -523,11 +523,11 @@
523 ** are listed, then only stash and revert the named files. The
524 ** "save" verb can be omitted if and only if there are no other
525 ** arguments. The "snapshot" verb works the same as "save" but
526 ** omits the revert, keeping the checkout unchanged.
527 **
528 ** > fossil stash list|ls ?-v|--verbose? ?-W|--width <num>?
529 **
530 ** List all changes sets currently stashed. Show information about
531 ** individual files in each changeset if -v or --verbose is used.
532 **
533 ** > fossil stash show|cat ?STASHID? ?DIFF-OPTIONS?
534
--- src/stash.c
+++ src/stash.c
@@ -523,11 +523,11 @@
523 ** are listed, then only stash and revert the named files. The
524 ** "save" verb can be omitted if and only if there are no other
525 ** arguments. The "snapshot" verb works the same as "save" but
526 ** omits the revert, keeping the checkout unchanged.
527 **
528 ** > fossil stash list|ls ?-v|--verbose? ?-W|--width NUM?
529 **
530 ** List all changes sets currently stashed. Show information about
531 ** individual files in each changeset if -v or --verbose is used.
532 **
533 ** > fossil stash show|cat ?STASHID? ?DIFF-OPTIONS?
534
+10 -125
--- src/style.c
+++ src/style.c
@@ -1230,16 +1230,16 @@
12301230
** element. If isChecked is true, the checkbox gets the "checked"
12311231
** attribute set, else it is not.
12321232
**
12331233
** Resulting structure:
12341234
**
1235
-** <span class='input-with-label' title={{zTip}} id={{zWrapperId}}>
1235
+** <div class='input-with-label' title={{zTip}} id={{zWrapperId}}>
12361236
** <input type='checkbox' name={{zFieldName}} value={{zValue}}
12371237
** id='A RANDOM VALUE'
12381238
** {{isChecked ? " checked : ""}}/>
12391239
** <label for='ID OF THE INPUT FIELD'>{{zLabel}}</label>
1240
-** </span>
1240
+** </div>
12411241
**
12421242
** zLabel, and zValue are required. zFieldName, zWrapperId, and zTip
12431243
** are may be NULL or empty.
12441244
**
12451245
** Be sure that the input-with-label CSS class is defined sensibly, in
@@ -1249,11 +1249,11 @@
12491249
void style_labeled_checkbox(const char * zWrapperId,
12501250
const char *zFieldName, const char * zLabel,
12511251
const char * zValue, int isChecked,
12521252
const char * zTip){
12531253
char * zLabelID = style_next_input_id();
1254
- CX("<span class='input-with-label'");
1254
+ CX("<div class='input-with-label'");
12551255
if(zTip && *zTip){
12561256
CX(" title='%h'", zTip);
12571257
}
12581258
if(zWrapperId && *zWrapperId){
12591259
CX(" id='%s'",zWrapperId);
@@ -1262,11 +1262,11 @@
12621262
if(zFieldName && *zFieldName){
12631263
CX("name='%s' ",zFieldName);
12641264
}
12651265
CX("value='%T'%s/>",
12661266
zValue ? zValue : "", isChecked ? " checked" : "");
1267
- CX("<label for='%s'>%h</label></span>", zLabelID, zLabel);
1267
+ CX("<label for='%s'>%h</label></div>", zLabelID, zLabel);
12681268
fossil_free(zLabelID);
12691269
}
12701270
12711271
/*
12721272
** Outputs a SELECT list from a compile-time list of integers.
@@ -1296,14 +1296,14 @@
12961296
**
12971297
** zTooltip is an optional value for the SELECT's title attribute.
12981298
**
12991299
** The structure of the emitted HTML is:
13001300
**
1301
-** <span class='input-with-label' title={{zToolTip}} id={{zWrapperId}}>
1301
+** <div class='input-with-label' title={{zToolTip}} id={{zWrapperId}}>
13021302
** <label for='SELECT ELEMENT ID'>{{zLabel}}</label>
13031303
** <select id='RANDOM ID' name={{zFieldName}}>...</select>
1304
-** </span>
1304
+** </div>
13051305
**
13061306
** Example:
13071307
**
13081308
** style_select_list_int("my-grapes", "my_grapes", "Grapes",
13091309
** "Select the number of grapes",
@@ -1318,11 +1318,11 @@
13181318
... ){
13191319
char * zLabelID = style_next_input_id();
13201320
va_list vargs;
13211321
13221322
va_start(vargs,selectedVal);
1323
- CX("<span class='input-with-label'");
1323
+ CX("<div class='input-with-label'");
13241324
if(zToolTip && *zToolTip){
13251325
CX(" title='%h'",zToolTip);
13261326
}
13271327
if(zWrapperId && *zWrapperId){
13281328
CX(" id='%s'",zWrapperId);
@@ -1347,11 +1347,11 @@
13471347
CX("%d",v);
13481348
}
13491349
CX("</option>\n");
13501350
}
13511351
CX("</select>\n");
1352
- CX("</span>\n");
1352
+ CX("</div>\n");
13531353
va_end(vargs);
13541354
fossil_free(zLabelID);
13551355
}
13561356
13571357
/*
@@ -1382,11 +1382,11 @@
13821382
13831383
va_start(vargs,zSelectedVal);
13841384
if(!zSelectedVal){
13851385
zSelectedVal = __FILE__/*some string we'll never match*/;
13861386
}
1387
- CX("<span class='input-with-label'");
1387
+ CX("<div class='input-with-label'");
13881388
if(zToolTip && *zToolTip){
13891389
CX(" title='%h'",zToolTip);
13901390
}
13911391
if(zWrapperId && *zWrapperId){
13921392
CX(" id='%s'",zWrapperId);
@@ -1411,100 +1411,15 @@
14111411
CX("%h",zVal);
14121412
}
14131413
CX("</option>\n");
14141414
}
14151415
CX("</select>\n");
1416
- CX("</span>\n");
1416
+ CX("</div>\n");
14171417
va_end(vargs);
14181418
fossil_free(zLabelID);
14191419
}
14201420
1421
-
1422
-/*
1423
-** The first time this is called, it emits code to install and
1424
-** bootstrap the window.fossil object, using the built-in file
1425
-** fossil.bootstrap.js (not to be confused with bootstrap.js).
1426
-**
1427
-** Subsequent calls are no-ops.
1428
-**
1429
-** It emits 2 parts:
1430
-**
1431
-** 1) window.fossil core object, some of which depends on C-level
1432
-** runtime data. That part of the script is always emitted inline. If
1433
-** addScriptTag is true then it is wrapped in its own SCRIPT tag, else
1434
-** it is assumed that the caller already opened a tag.
1435
-**
1436
-** 2) Emits the static fossil.bootstrap.js using builtin_request_js().
1437
-*/
1438
-void style_emit_script_fossil_bootstrap(int addScriptTag){
1439
- static int once = 0;
1440
- if(0==once++){
1441
- char * zName;
1442
- /* Set up the generic/app-agnostic parts of window.fossil
1443
- ** which require C-level state... */
1444
- if(addScriptTag!=0){
1445
- style_emit_script_tag(0,0);
1446
- }
1447
- CX("(function(){\n");
1448
- CX(/*MSIE NodeList.forEach polyfill, courtesy of Mozilla:
1449
- https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach#Polyfill
1450
- */
1451
- "if(window.NodeList && !NodeList.prototype.forEach){"
1452
- "NodeList.prototype.forEach = Array.prototype.forEach;"
1453
- "}\n");
1454
- CX("if(!window.fossil) window.fossil={};\n"
1455
- "window.fossil.version = %!j;\n"
1456
- /* fossil.rootPath is the top-most CGI/server path,
1457
- ** including a trailing slash. */
1458
- "window.fossil.rootPath = %!j+'/';\n",
1459
- get_version(), g.zTop);
1460
- /* fossil.config = {...various config-level options...} */
1461
- CX("window.fossil.config = {");
1462
- zName = db_get("project-name", "");
1463
- CX("projectName: %!j,\n", zName);
1464
- fossil_free(zName);
1465
- zName = db_get("short-project-name", "");
1466
- CX("shortProjectName: %!j,\n", zName);
1467
- fossil_free(zName);
1468
- zName = db_get("project-code", "");
1469
- CX("projectCode: %!j,\n", zName);
1470
- fossil_free(zName);
1471
- CX("/* Length of UUID hashes for display purposes. */");
1472
- CX("hashDigits: %d, hashDigitsUrl: %d,\n",
1473
- hash_digits(0), hash_digits(1));
1474
- CX("editStateMarkers: {"
1475
- "/*Symbolic markers to denote certain edit states.*/"
1476
- "isNew:'[+]', isModified:'[*]', isDeleted:'[-]'},\n");
1477
- CX("confirmerButtonTicks: 3 "
1478
- "/*default fossil.confirmer tick count.*/\n");
1479
- CX("};\n"/* fossil.config */);
1480
-#if 0
1481
- /* Is it safe to emit the CSRF token here? Some pages add it
1482
- ** as a hidden form field. */
1483
- if(g.zCsrfToken[0]!=0){
1484
- CX("window.fossil.csrfToken = %!j;\n",
1485
- g.zCsrfToken);
1486
- }
1487
-#endif
1488
- /*
1489
- ** fossil.page holds info about the current page. This is also
1490
- ** where the current page "should" store any of its own
1491
- ** page-specific state, and it is reserved for that purpose.
1492
- */
1493
- CX("window.fossil.page = {"
1494
- "name:\"%T\""
1495
- "};\n", g.zPath);
1496
- CX("})();\n");
1497
- if(addScriptTag!=0){
1498
- style_emit_script_tag(1,0);
1499
- }
1500
- /* The remaining window.fossil bootstrap code is not dependent on
1501
- ** C-runtime state... */
1502
- builtin_request_js("fossil.bootstrap.js");
1503
- }
1504
-}
1505
-
15061421
/*
15071422
** If passed 0 as its first argument, it emits a script opener tag
15081423
** with this request's nonce. If passed non-0 it emits a script
15091424
** closing tag. Mnemonic for remembering the order in which to pass 0
15101425
** or 1 as the first argument to this function: 0 comes before 1.
@@ -1530,35 +1445,5 @@
15301445
}
15311446
}else{
15321447
CX("</script>\n");
15331448
}
15341449
}
1535
-
1536
-/*
1537
-** Convenience wrapper which calls builtin_request_js() for a series
1538
-** of builtin scripts named fossil.NAME.js. The first time it is
1539
-** called, it also calls style_emit_script_fossil_bootstrap() to
1540
-** initialize the window.fossil JS API. The first argument is a
1541
-** no-meaning dummy required by the va_start() interface. All
1542
-** subsequent arguments must be strings of the NAME part of
1543
-** fossil.NAME.js, followed by a NULL argument to terminate the list.
1544
-**
1545
-** e.g. pass it (0, "fetch", "dom", "tabs", 0) to load those 3
1546
-** APIs. Do not forget the trailing 0!
1547
-*/
1548
-void style_emit_fossil_js_apis( int dummy, ... ) {
1549
- static int once = 0;
1550
- const char *zArg;
1551
- char * zName;
1552
- va_list vargs;
1553
-
1554
- if(0==once++){
1555
- style_emit_script_fossil_bootstrap(1);
1556
- }
1557
- va_start(vargs,dummy);
1558
- while( (zArg = va_arg (vargs, const char *))!=0 ){
1559
- zName = mprintf("fossil.%s.js", zArg);
1560
- builtin_request_js(zName);
1561
- fossil_free(zName);
1562
- }
1563
- va_end(vargs);
1564
-}
15651450
--- src/style.c
+++ src/style.c
@@ -1230,16 +1230,16 @@
1230 ** element. If isChecked is true, the checkbox gets the "checked"
1231 ** attribute set, else it is not.
1232 **
1233 ** Resulting structure:
1234 **
1235 ** <span class='input-with-label' title={{zTip}} id={{zWrapperId}}>
1236 ** <input type='checkbox' name={{zFieldName}} value={{zValue}}
1237 ** id='A RANDOM VALUE'
1238 ** {{isChecked ? " checked : ""}}/>
1239 ** <label for='ID OF THE INPUT FIELD'>{{zLabel}}</label>
1240 ** </span>
1241 **
1242 ** zLabel, and zValue are required. zFieldName, zWrapperId, and zTip
1243 ** are may be NULL or empty.
1244 **
1245 ** Be sure that the input-with-label CSS class is defined sensibly, in
@@ -1249,11 +1249,11 @@
1249 void style_labeled_checkbox(const char * zWrapperId,
1250 const char *zFieldName, const char * zLabel,
1251 const char * zValue, int isChecked,
1252 const char * zTip){
1253 char * zLabelID = style_next_input_id();
1254 CX("<span class='input-with-label'");
1255 if(zTip && *zTip){
1256 CX(" title='%h'", zTip);
1257 }
1258 if(zWrapperId && *zWrapperId){
1259 CX(" id='%s'",zWrapperId);
@@ -1262,11 +1262,11 @@
1262 if(zFieldName && *zFieldName){
1263 CX("name='%s' ",zFieldName);
1264 }
1265 CX("value='%T'%s/>",
1266 zValue ? zValue : "", isChecked ? " checked" : "");
1267 CX("<label for='%s'>%h</label></span>", zLabelID, zLabel);
1268 fossil_free(zLabelID);
1269 }
1270
1271 /*
1272 ** Outputs a SELECT list from a compile-time list of integers.
@@ -1296,14 +1296,14 @@
1296 **
1297 ** zTooltip is an optional value for the SELECT's title attribute.
1298 **
1299 ** The structure of the emitted HTML is:
1300 **
1301 ** <span class='input-with-label' title={{zToolTip}} id={{zWrapperId}}>
1302 ** <label for='SELECT ELEMENT ID'>{{zLabel}}</label>
1303 ** <select id='RANDOM ID' name={{zFieldName}}>...</select>
1304 ** </span>
1305 **
1306 ** Example:
1307 **
1308 ** style_select_list_int("my-grapes", "my_grapes", "Grapes",
1309 ** "Select the number of grapes",
@@ -1318,11 +1318,11 @@
1318 ... ){
1319 char * zLabelID = style_next_input_id();
1320 va_list vargs;
1321
1322 va_start(vargs,selectedVal);
1323 CX("<span class='input-with-label'");
1324 if(zToolTip && *zToolTip){
1325 CX(" title='%h'",zToolTip);
1326 }
1327 if(zWrapperId && *zWrapperId){
1328 CX(" id='%s'",zWrapperId);
@@ -1347,11 +1347,11 @@
1347 CX("%d",v);
1348 }
1349 CX("</option>\n");
1350 }
1351 CX("</select>\n");
1352 CX("</span>\n");
1353 va_end(vargs);
1354 fossil_free(zLabelID);
1355 }
1356
1357 /*
@@ -1382,11 +1382,11 @@
1382
1383 va_start(vargs,zSelectedVal);
1384 if(!zSelectedVal){
1385 zSelectedVal = __FILE__/*some string we'll never match*/;
1386 }
1387 CX("<span class='input-with-label'");
1388 if(zToolTip && *zToolTip){
1389 CX(" title='%h'",zToolTip);
1390 }
1391 if(zWrapperId && *zWrapperId){
1392 CX(" id='%s'",zWrapperId);
@@ -1411,100 +1411,15 @@
1411 CX("%h",zVal);
1412 }
1413 CX("</option>\n");
1414 }
1415 CX("</select>\n");
1416 CX("</span>\n");
1417 va_end(vargs);
1418 fossil_free(zLabelID);
1419 }
1420
1421
1422 /*
1423 ** The first time this is called, it emits code to install and
1424 ** bootstrap the window.fossil object, using the built-in file
1425 ** fossil.bootstrap.js (not to be confused with bootstrap.js).
1426 **
1427 ** Subsequent calls are no-ops.
1428 **
1429 ** It emits 2 parts:
1430 **
1431 ** 1) window.fossil core object, some of which depends on C-level
1432 ** runtime data. That part of the script is always emitted inline. If
1433 ** addScriptTag is true then it is wrapped in its own SCRIPT tag, else
1434 ** it is assumed that the caller already opened a tag.
1435 **
1436 ** 2) Emits the static fossil.bootstrap.js using builtin_request_js().
1437 */
1438 void style_emit_script_fossil_bootstrap(int addScriptTag){
1439 static int once = 0;
1440 if(0==once++){
1441 char * zName;
1442 /* Set up the generic/app-agnostic parts of window.fossil
1443 ** which require C-level state... */
1444 if(addScriptTag!=0){
1445 style_emit_script_tag(0,0);
1446 }
1447 CX("(function(){\n");
1448 CX(/*MSIE NodeList.forEach polyfill, courtesy of Mozilla:
1449 https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach#Polyfill
1450 */
1451 "if(window.NodeList && !NodeList.prototype.forEach){"
1452 "NodeList.prototype.forEach = Array.prototype.forEach;"
1453 "}\n");
1454 CX("if(!window.fossil) window.fossil={};\n"
1455 "window.fossil.version = %!j;\n"
1456 /* fossil.rootPath is the top-most CGI/server path,
1457 ** including a trailing slash. */
1458 "window.fossil.rootPath = %!j+'/';\n",
1459 get_version(), g.zTop);
1460 /* fossil.config = {...various config-level options...} */
1461 CX("window.fossil.config = {");
1462 zName = db_get("project-name", "");
1463 CX("projectName: %!j,\n", zName);
1464 fossil_free(zName);
1465 zName = db_get("short-project-name", "");
1466 CX("shortProjectName: %!j,\n", zName);
1467 fossil_free(zName);
1468 zName = db_get("project-code", "");
1469 CX("projectCode: %!j,\n", zName);
1470 fossil_free(zName);
1471 CX("/* Length of UUID hashes for display purposes. */");
1472 CX("hashDigits: %d, hashDigitsUrl: %d,\n",
1473 hash_digits(0), hash_digits(1));
1474 CX("editStateMarkers: {"
1475 "/*Symbolic markers to denote certain edit states.*/"
1476 "isNew:'[+]', isModified:'[*]', isDeleted:'[-]'},\n");
1477 CX("confirmerButtonTicks: 3 "
1478 "/*default fossil.confirmer tick count.*/\n");
1479 CX("};\n"/* fossil.config */);
1480 #if 0
1481 /* Is it safe to emit the CSRF token here? Some pages add it
1482 ** as a hidden form field. */
1483 if(g.zCsrfToken[0]!=0){
1484 CX("window.fossil.csrfToken = %!j;\n",
1485 g.zCsrfToken);
1486 }
1487 #endif
1488 /*
1489 ** fossil.page holds info about the current page. This is also
1490 ** where the current page "should" store any of its own
1491 ** page-specific state, and it is reserved for that purpose.
1492 */
1493 CX("window.fossil.page = {"
1494 "name:\"%T\""
1495 "};\n", g.zPath);
1496 CX("})();\n");
1497 if(addScriptTag!=0){
1498 style_emit_script_tag(1,0);
1499 }
1500 /* The remaining window.fossil bootstrap code is not dependent on
1501 ** C-runtime state... */
1502 builtin_request_js("fossil.bootstrap.js");
1503 }
1504 }
1505
1506 /*
1507 ** If passed 0 as its first argument, it emits a script opener tag
1508 ** with this request's nonce. If passed non-0 it emits a script
1509 ** closing tag. Mnemonic for remembering the order in which to pass 0
1510 ** or 1 as the first argument to this function: 0 comes before 1.
@@ -1530,35 +1445,5 @@
1530 }
1531 }else{
1532 CX("</script>\n");
1533 }
1534 }
1535
1536 /*
1537 ** Convenience wrapper which calls builtin_request_js() for a series
1538 ** of builtin scripts named fossil.NAME.js. The first time it is
1539 ** called, it also calls style_emit_script_fossil_bootstrap() to
1540 ** initialize the window.fossil JS API. The first argument is a
1541 ** no-meaning dummy required by the va_start() interface. All
1542 ** subsequent arguments must be strings of the NAME part of
1543 ** fossil.NAME.js, followed by a NULL argument to terminate the list.
1544 **
1545 ** e.g. pass it (0, "fetch", "dom", "tabs", 0) to load those 3
1546 ** APIs. Do not forget the trailing 0!
1547 */
1548 void style_emit_fossil_js_apis( int dummy, ... ) {
1549 static int once = 0;
1550 const char *zArg;
1551 char * zName;
1552 va_list vargs;
1553
1554 if(0==once++){
1555 style_emit_script_fossil_bootstrap(1);
1556 }
1557 va_start(vargs,dummy);
1558 while( (zArg = va_arg (vargs, const char *))!=0 ){
1559 zName = mprintf("fossil.%s.js", zArg);
1560 builtin_request_js(zName);
1561 fossil_free(zName);
1562 }
1563 va_end(vargs);
1564 }
1565
--- src/style.c
+++ src/style.c
@@ -1230,16 +1230,16 @@
1230 ** element. If isChecked is true, the checkbox gets the "checked"
1231 ** attribute set, else it is not.
1232 **
1233 ** Resulting structure:
1234 **
1235 ** <div class='input-with-label' title={{zTip}} id={{zWrapperId}}>
1236 ** <input type='checkbox' name={{zFieldName}} value={{zValue}}
1237 ** id='A RANDOM VALUE'
1238 ** {{isChecked ? " checked : ""}}/>
1239 ** <label for='ID OF THE INPUT FIELD'>{{zLabel}}</label>
1240 ** </div>
1241 **
1242 ** zLabel, and zValue are required. zFieldName, zWrapperId, and zTip
1243 ** are may be NULL or empty.
1244 **
1245 ** Be sure that the input-with-label CSS class is defined sensibly, in
@@ -1249,11 +1249,11 @@
1249 void style_labeled_checkbox(const char * zWrapperId,
1250 const char *zFieldName, const char * zLabel,
1251 const char * zValue, int isChecked,
1252 const char * zTip){
1253 char * zLabelID = style_next_input_id();
1254 CX("<div class='input-with-label'");
1255 if(zTip && *zTip){
1256 CX(" title='%h'", zTip);
1257 }
1258 if(zWrapperId && *zWrapperId){
1259 CX(" id='%s'",zWrapperId);
@@ -1262,11 +1262,11 @@
1262 if(zFieldName && *zFieldName){
1263 CX("name='%s' ",zFieldName);
1264 }
1265 CX("value='%T'%s/>",
1266 zValue ? zValue : "", isChecked ? " checked" : "");
1267 CX("<label for='%s'>%h</label></div>", zLabelID, zLabel);
1268 fossil_free(zLabelID);
1269 }
1270
1271 /*
1272 ** Outputs a SELECT list from a compile-time list of integers.
@@ -1296,14 +1296,14 @@
1296 **
1297 ** zTooltip is an optional value for the SELECT's title attribute.
1298 **
1299 ** The structure of the emitted HTML is:
1300 **
1301 ** <div class='input-with-label' title={{zToolTip}} id={{zWrapperId}}>
1302 ** <label for='SELECT ELEMENT ID'>{{zLabel}}</label>
1303 ** <select id='RANDOM ID' name={{zFieldName}}>...</select>
1304 ** </div>
1305 **
1306 ** Example:
1307 **
1308 ** style_select_list_int("my-grapes", "my_grapes", "Grapes",
1309 ** "Select the number of grapes",
@@ -1318,11 +1318,11 @@
1318 ... ){
1319 char * zLabelID = style_next_input_id();
1320 va_list vargs;
1321
1322 va_start(vargs,selectedVal);
1323 CX("<div class='input-with-label'");
1324 if(zToolTip && *zToolTip){
1325 CX(" title='%h'",zToolTip);
1326 }
1327 if(zWrapperId && *zWrapperId){
1328 CX(" id='%s'",zWrapperId);
@@ -1347,11 +1347,11 @@
1347 CX("%d",v);
1348 }
1349 CX("</option>\n");
1350 }
1351 CX("</select>\n");
1352 CX("</div>\n");
1353 va_end(vargs);
1354 fossil_free(zLabelID);
1355 }
1356
1357 /*
@@ -1382,11 +1382,11 @@
1382
1383 va_start(vargs,zSelectedVal);
1384 if(!zSelectedVal){
1385 zSelectedVal = __FILE__/*some string we'll never match*/;
1386 }
1387 CX("<div class='input-with-label'");
1388 if(zToolTip && *zToolTip){
1389 CX(" title='%h'",zToolTip);
1390 }
1391 if(zWrapperId && *zWrapperId){
1392 CX(" id='%s'",zWrapperId);
@@ -1411,100 +1411,15 @@
1411 CX("%h",zVal);
1412 }
1413 CX("</option>\n");
1414 }
1415 CX("</select>\n");
1416 CX("</div>\n");
1417 va_end(vargs);
1418 fossil_free(zLabelID);
1419 }
1420
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1421 /*
1422 ** If passed 0 as its first argument, it emits a script opener tag
1423 ** with this request's nonce. If passed non-0 it emits a script
1424 ** closing tag. Mnemonic for remembering the order in which to pass 0
1425 ** or 1 as the first argument to this function: 0 comes before 1.
@@ -1530,35 +1445,5 @@
1445 }
1446 }else{
1447 CX("</script>\n");
1448 }
1449 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1450
--- src/style.fileedit.css
+++ src/style.fileedit.css
@@ -220,6 +220,15 @@
220220
body.fileedit #fileedit-edit-status span.links > *::before {
221221
content: "[";
222222
}
223223
body.fileedit #fileedit-edit-status span.links > *::after {
224224
content: "]";
225
+}
226
+/* JS selection of line numbers cannot work in preview mode,
227
+ so disable the UI indications which imply that it does
228
+ something... */
229
+body.fileedit table.numbered-lines td.line-numbers > span {
230
+ cursor: unset;
231
+}
232
+body.fileedit table.numbered-lines td.line-numbers > span:hover {
233
+ background-color: inherit;
225234
}
226235
--- src/style.fileedit.css
+++ src/style.fileedit.css
@@ -220,6 +220,15 @@
220 body.fileedit #fileedit-edit-status span.links > *::before {
221 content: "[";
222 }
223 body.fileedit #fileedit-edit-status span.links > *::after {
224 content: "]";
 
 
 
 
 
 
 
 
 
225 }
226
--- src/style.fileedit.css
+++ src/style.fileedit.css
@@ -220,6 +220,15 @@
220 body.fileedit #fileedit-edit-status span.links > *::before {
221 content: "[";
222 }
223 body.fileedit #fileedit-edit-status span.links > *::after {
224 content: "]";
225 }
226 /* JS selection of line numbers cannot work in preview mode,
227 so disable the UI indications which imply that it does
228 something... */
229 body.fileedit table.numbered-lines td.line-numbers > span {
230 cursor: unset;
231 }
232 body.fileedit table.numbered-lines td.line-numbers > span:hover {
233 background-color: inherit;
234 }
235
--- src/style.wikiedit.css
+++ src/style.wikiedit.css
@@ -184,11 +184,16 @@
184184
flex-direction: row;
185185
flex-wrap: wrap;
186186
align-items: baseline;
187187
}
188188
body.wikiedit #wikiedit-stash-selector select {
189
- margin: 0 1em;
189
+ margin: 0 1em 0 0.5em;
190190
height: initial;
191191
font-family: monospace;
192192
flex: 10 1 auto;
193193
}
194
-
194
+body.wikiedit fieldset.page-types-list > div > span {
195
+ display: flex;
196
+ flex-direction: row;
197
+ flex-wrap: nowrap;
198
+ align-items: center;
199
+}
195200
--- src/style.wikiedit.css
+++ src/style.wikiedit.css
@@ -184,11 +184,16 @@
184 flex-direction: row;
185 flex-wrap: wrap;
186 align-items: baseline;
187 }
188 body.wikiedit #wikiedit-stash-selector select {
189 margin: 0 1em;
190 height: initial;
191 font-family: monospace;
192 flex: 10 1 auto;
193 }
194
 
 
 
 
 
195
--- src/style.wikiedit.css
+++ src/style.wikiedit.css
@@ -184,11 +184,16 @@
184 flex-direction: row;
185 flex-wrap: wrap;
186 align-items: baseline;
187 }
188 body.wikiedit #wikiedit-stash-selector select {
189 margin: 0 1em 0 0.5em;
190 height: initial;
191 font-family: monospace;
192 flex: 10 1 auto;
193 }
194 body.wikiedit fieldset.page-types-list > div > span {
195 display: flex;
196 flex-direction: row;
197 flex-wrap: nowrap;
198 align-items: center;
199 }
200
+4 -3
--- src/timeline.c
+++ src/timeline.c
@@ -2858,13 +2858,14 @@
28582858
** t = tickets only
28592859
** w = wiki commits only
28602860
** -v|--verbose Output the list of files changed by each commit
28612861
** and the type of each change (edited, deleted,
28622862
** etc.) after the check-in comment.
2863
-** -W|--width <num> Width of lines (default is to auto-detect). Must be
2864
-** >20 or 0 (= no limit, resulting in a single line per
2865
-** entry).
2863
+** -W|--width N Width of lines (default is to auto-detect). N must be
2864
+** either greater than 20 or it ust be zero 0 to
2865
+** indicate no limit, resulting in a single line per
2866
+** entry.
28662867
** -R REPO_FILE Specifies the repository db to use. Default is
28672868
** the current checkout's repository.
28682869
*/
28692870
void timeline_cmd(void){
28702871
Stmt q;
28712872
--- src/timeline.c
+++ src/timeline.c
@@ -2858,13 +2858,14 @@
2858 ** t = tickets only
2859 ** w = wiki commits only
2860 ** -v|--verbose Output the list of files changed by each commit
2861 ** and the type of each change (edited, deleted,
2862 ** etc.) after the check-in comment.
2863 ** -W|--width <num> Width of lines (default is to auto-detect). Must be
2864 ** >20 or 0 (= no limit, resulting in a single line per
2865 ** entry).
 
2866 ** -R REPO_FILE Specifies the repository db to use. Default is
2867 ** the current checkout's repository.
2868 */
2869 void timeline_cmd(void){
2870 Stmt q;
2871
--- src/timeline.c
+++ src/timeline.c
@@ -2858,13 +2858,14 @@
2858 ** t = tickets only
2859 ** w = wiki commits only
2860 ** -v|--verbose Output the list of files changed by each commit
2861 ** and the type of each change (edited, deleted,
2862 ** etc.) after the check-in comment.
2863 ** -W|--width N Width of lines (default is to auto-detect). N must be
2864 ** either greater than 20 or it ust be zero 0 to
2865 ** indicate no limit, resulting in a single line per
2866 ** entry.
2867 ** -R REPO_FILE Specifies the repository db to use. Default is
2868 ** the current checkout's repository.
2869 */
2870 void timeline_cmd(void){
2871 Stmt q;
2872
+36 -18
--- src/wiki.c
+++ src/wiki.c
@@ -1162,33 +1162,45 @@
11621162
"data-tab-parent='wikiedit-tabs' "
11631163
"data-tab-label='Editor' "
11641164
"class='hidden'"
11651165
">");
11661166
CX("<div class='flex-container flex-row child-gap-small'>");
1167
- CX("<span class='input-with-label'>"
1167
+ CX("<div class='input-with-label'>"
11681168
"<label>Mime type</label>");
11691169
mimetype_option_menu(0);
1170
- CX("</span>");
1170
+ CX("</div>");
11711171
style_select_list_int("select-font-size",
11721172
"editor_font_size", "Editor font size",
11731173
NULL/*tooltip*/,
11741174
100,
11751175
"100%", 100, "125%", 125,
11761176
"150%", 150, "175%", 175,
11771177
"200%", 200, NULL);
1178
- CX("<button class='wikiedit-save'>"
1178
+ CX("<div class='input-with-label'>"
1179
+ "<button class='wikiedit-save'>"
11791180
"Save</button>"
1180
- /*will get moved around dynamically*/);
1181
- CX("<button class='wikiedit-save-close'>"
1182
- "Save &amp; Close</button>"/*will get moved around dynamically*/);
1181
+ "</div>" /*will get moved around dynamically*/);
1182
+ CX("<div class='input-with-label'>"
1183
+ "<button class='wikiedit-save-close'>"
1184
+ "Save &amp; Close</button>"
1185
+ "<div class='help-buttonlet'>"
1186
+ "Save edits to this page and returns to the wiki page viewer."
1187
+ "</div>"
1188
+ "</div>" /*will get moved around dynamically*/);
11831189
CX("<span class='save-button-slot'></span>");
1184
- CX("<button class='wikiedit-content-reload' "
1185
- "title='Reload the file from the server, discarding "
1190
+
1191
+ CX("<div class='input-with-label'>"
1192
+ "<button class='wikiedit-content-reload' "
1193
+ ">Discard &amp; Reload</button>"
1194
+ "<div class='help-buttonlet'>"
1195
+ "Reload the file from the server, discarding "
11861196
"any local edits. To help avoid accidental loss of "
11871197
"edits, it requires confirmation (a second click) within "
1188
- "a few seconds or it will not reload.'"
1189
- ">Discard &amp; Reload</button>");
1198
+ "a few seconds or it will not reload."
1199
+ "</div>"
1200
+ "</div>");
1201
+
11901202
CX("</div>");
11911203
CX("<div class='flex-container flex-column stretch'>");
11921204
CX("<textarea name='content' id='wikiedit-content-editor' "
11931205
"class='wikiedit' rows='25'>");
11941206
CX("</textarea>");
@@ -1213,16 +1225,19 @@
12131225
/* ^^^ fossil.page[methodName](content, callback) */
12141226
"data-f-preview-to='#wikiedit-tab-preview-wrapper' "
12151227
/* ^^^ dest elem ID */
12161228
">Refresh</button>");
12171229
/* Toggle auto-update of preview when the Preview tab is selected. */
1218
- style_labeled_checkbox("cb-preview-autoupdate",
1219
- NULL,
1220
- "Auto-refresh?",
1221
- "1", 1,
1222
- "If on, the preview will automatically "
1223
- "refresh when this tab is selected.");
1230
+ CX("<div class='input-with-label'>"
1231
+ "<input type='checkbox' value='1' "
1232
+ "id='cb-preview-autorefresh' checked>"
1233
+ "<label for='cb-preview-autorefresh'>Auto-refresh?</label>"
1234
+ "<div class='help-buttonlet'>"
1235
+ "If on, the preview will automatically "
1236
+ "refresh (if needed) when this tab is selected."
1237
+ "</div>"
1238
+ "</div>");
12241239
CX("<span class='save-button-slot'></span>");
12251240
CX("</div>"/*.wikiedit-options*/);
12261241
CX("<div id='wikiedit-tab-preview-wrapper'></div>");
12271242
CX("</div>"/*#wikiedit-tab-preview*/);
12281243
}
@@ -1270,13 +1285,16 @@
12701285
CX("<h2>Wiki Name Rules</h2>");
12711286
well_formed_wiki_name_rules();
12721287
CX("</div>"/*#wikiedit-tab-save*/);
12731288
}
12741289
1290
+ if(!builtin_bundle_all_fossil_js_apis()){
1291
+ builtin_emit_fossil_js_apis("fetch", "dom", "tabs", "confirmer",
1292
+ "storage", "popupwidget", 0);
1293
+ }
12751294
builtin_request_js("sbsdiff.js");
1276
- style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer",
1277
- "storage", "page.wikiedit", 0);
1295
+ builtin_request_js("fossil.page.wikiedit.js");
12781296
builtin_fulfill_js_requests();
12791297
/* Dynamically populate the editor... */
12801298
style_emit_script_tag(0,0);
12811299
{
12821300
/* Render the current page list to save us an XHR request
12831301
--- src/wiki.c
+++ src/wiki.c
@@ -1162,33 +1162,45 @@
1162 "data-tab-parent='wikiedit-tabs' "
1163 "data-tab-label='Editor' "
1164 "class='hidden'"
1165 ">");
1166 CX("<div class='flex-container flex-row child-gap-small'>");
1167 CX("<span class='input-with-label'>"
1168 "<label>Mime type</label>");
1169 mimetype_option_menu(0);
1170 CX("</span>");
1171 style_select_list_int("select-font-size",
1172 "editor_font_size", "Editor font size",
1173 NULL/*tooltip*/,
1174 100,
1175 "100%", 100, "125%", 125,
1176 "150%", 150, "175%", 175,
1177 "200%", 200, NULL);
1178 CX("<button class='wikiedit-save'>"
 
1179 "Save</button>"
1180 /*will get moved around dynamically*/);
1181 CX("<button class='wikiedit-save-close'>"
1182 "Save &amp; Close</button>"/*will get moved around dynamically*/);
 
 
 
 
 
1183 CX("<span class='save-button-slot'></span>");
1184 CX("<button class='wikiedit-content-reload' "
1185 "title='Reload the file from the server, discarding "
 
 
 
 
1186 "any local edits. To help avoid accidental loss of "
1187 "edits, it requires confirmation (a second click) within "
1188 "a few seconds or it will not reload.'"
1189 ">Discard &amp; Reload</button>");
 
 
1190 CX("</div>");
1191 CX("<div class='flex-container flex-column stretch'>");
1192 CX("<textarea name='content' id='wikiedit-content-editor' "
1193 "class='wikiedit' rows='25'>");
1194 CX("</textarea>");
@@ -1213,16 +1225,19 @@
1213 /* ^^^ fossil.page[methodName](content, callback) */
1214 "data-f-preview-to='#wikiedit-tab-preview-wrapper' "
1215 /* ^^^ dest elem ID */
1216 ">Refresh</button>");
1217 /* Toggle auto-update of preview when the Preview tab is selected. */
1218 style_labeled_checkbox("cb-preview-autoupdate",
1219 NULL,
1220 "Auto-refresh?",
1221 "1", 1,
1222 "If on, the preview will automatically "
1223 "refresh when this tab is selected.");
 
 
 
1224 CX("<span class='save-button-slot'></span>");
1225 CX("</div>"/*.wikiedit-options*/);
1226 CX("<div id='wikiedit-tab-preview-wrapper'></div>");
1227 CX("</div>"/*#wikiedit-tab-preview*/);
1228 }
@@ -1270,13 +1285,16 @@
1270 CX("<h2>Wiki Name Rules</h2>");
1271 well_formed_wiki_name_rules();
1272 CX("</div>"/*#wikiedit-tab-save*/);
1273 }
1274
 
 
 
 
1275 builtin_request_js("sbsdiff.js");
1276 style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer",
1277 "storage", "page.wikiedit", 0);
1278 builtin_fulfill_js_requests();
1279 /* Dynamically populate the editor... */
1280 style_emit_script_tag(0,0);
1281 {
1282 /* Render the current page list to save us an XHR request
1283
--- src/wiki.c
+++ src/wiki.c
@@ -1162,33 +1162,45 @@
1162 "data-tab-parent='wikiedit-tabs' "
1163 "data-tab-label='Editor' "
1164 "class='hidden'"
1165 ">");
1166 CX("<div class='flex-container flex-row child-gap-small'>");
1167 CX("<div class='input-with-label'>"
1168 "<label>Mime type</label>");
1169 mimetype_option_menu(0);
1170 CX("</div>");
1171 style_select_list_int("select-font-size",
1172 "editor_font_size", "Editor font size",
1173 NULL/*tooltip*/,
1174 100,
1175 "100%", 100, "125%", 125,
1176 "150%", 150, "175%", 175,
1177 "200%", 200, NULL);
1178 CX("<div class='input-with-label'>"
1179 "<button class='wikiedit-save'>"
1180 "Save</button>"
1181 "</div>" /*will get moved around dynamically*/);
1182 CX("<div class='input-with-label'>"
1183 "<button class='wikiedit-save-close'>"
1184 "Save &amp; Close</button>"
1185 "<div class='help-buttonlet'>"
1186 "Save edits to this page and returns to the wiki page viewer."
1187 "</div>"
1188 "</div>" /*will get moved around dynamically*/);
1189 CX("<span class='save-button-slot'></span>");
1190
1191 CX("<div class='input-with-label'>"
1192 "<button class='wikiedit-content-reload' "
1193 ">Discard &amp; Reload</button>"
1194 "<div class='help-buttonlet'>"
1195 "Reload the file from the server, discarding "
1196 "any local edits. To help avoid accidental loss of "
1197 "edits, it requires confirmation (a second click) within "
1198 "a few seconds or it will not reload."
1199 "</div>"
1200 "</div>");
1201
1202 CX("</div>");
1203 CX("<div class='flex-container flex-column stretch'>");
1204 CX("<textarea name='content' id='wikiedit-content-editor' "
1205 "class='wikiedit' rows='25'>");
1206 CX("</textarea>");
@@ -1213,16 +1225,19 @@
1225 /* ^^^ fossil.page[methodName](content, callback) */
1226 "data-f-preview-to='#wikiedit-tab-preview-wrapper' "
1227 /* ^^^ dest elem ID */
1228 ">Refresh</button>");
1229 /* Toggle auto-update of preview when the Preview tab is selected. */
1230 CX("<div class='input-with-label'>"
1231 "<input type='checkbox' value='1' "
1232 "id='cb-preview-autorefresh' checked>"
1233 "<label for='cb-preview-autorefresh'>Auto-refresh?</label>"
1234 "<div class='help-buttonlet'>"
1235 "If on, the preview will automatically "
1236 "refresh (if needed) when this tab is selected."
1237 "</div>"
1238 "</div>");
1239 CX("<span class='save-button-slot'></span>");
1240 CX("</div>"/*.wikiedit-options*/);
1241 CX("<div id='wikiedit-tab-preview-wrapper'></div>");
1242 CX("</div>"/*#wikiedit-tab-preview*/);
1243 }
@@ -1270,13 +1285,16 @@
1285 CX("<h2>Wiki Name Rules</h2>");
1286 well_formed_wiki_name_rules();
1287 CX("</div>"/*#wikiedit-tab-save*/);
1288 }
1289
1290 if(!builtin_bundle_all_fossil_js_apis()){
1291 builtin_emit_fossil_js_apis("fetch", "dom", "tabs", "confirmer",
1292 "storage", "popupwidget", 0);
1293 }
1294 builtin_request_js("sbsdiff.js");
1295 builtin_request_js("fossil.page.wikiedit.js");
 
1296 builtin_fulfill_js_requests();
1297 /* Dynamically populate the editor... */
1298 style_emit_script_tag(0,0);
1299 {
1300 /* Render the current page list to save us an XHR request
1301
+36 -18
--- src/wiki.c
+++ src/wiki.c
@@ -1162,33 +1162,45 @@
11621162
"data-tab-parent='wikiedit-tabs' "
11631163
"data-tab-label='Editor' "
11641164
"class='hidden'"
11651165
">");
11661166
CX("<div class='flex-container flex-row child-gap-small'>");
1167
- CX("<span class='input-with-label'>"
1167
+ CX("<div class='input-with-label'>"
11681168
"<label>Mime type</label>");
11691169
mimetype_option_menu(0);
1170
- CX("</span>");
1170
+ CX("</div>");
11711171
style_select_list_int("select-font-size",
11721172
"editor_font_size", "Editor font size",
11731173
NULL/*tooltip*/,
11741174
100,
11751175
"100%", 100, "125%", 125,
11761176
"150%", 150, "175%", 175,
11771177
"200%", 200, NULL);
1178
- CX("<button class='wikiedit-save'>"
1178
+ CX("<div class='input-with-label'>"
1179
+ "<button class='wikiedit-save'>"
11791180
"Save</button>"
1180
- /*will get moved around dynamically*/);
1181
- CX("<button class='wikiedit-save-close'>"
1182
- "Save &amp; Close</button>"/*will get moved around dynamically*/);
1181
+ "</div>" /*will get moved around dynamically*/);
1182
+ CX("<div class='input-with-label'>"
1183
+ "<button class='wikiedit-save-close'>"
1184
+ "Save &amp; Close</button>"
1185
+ "<div class='help-buttonlet'>"
1186
+ "Save edits to this page and returns to the wiki page viewer."
1187
+ "</div>"
1188
+ "</div>" /*will get moved around dynamically*/);
11831189
CX("<span class='save-button-slot'></span>");
1184
- CX("<button class='wikiedit-content-reload' "
1185
- "title='Reload the file from the server, discarding "
1190
+
1191
+ CX("<div class='input-with-label'>"
1192
+ "<button class='wikiedit-content-reload' "
1193
+ ">Discard &amp; Reload</button>"
1194
+ "<div class='help-buttonlet'>"
1195
+ "Reload the file from the server, discarding "
11861196
"any local edits. To help avoid accidental loss of "
11871197
"edits, it requires confirmation (a second click) within "
1188
- "a few seconds or it will not reload.'"
1189
- ">Discard &amp; Reload</button>");
1198
+ "a few seconds or it will not reload."
1199
+ "</div>"
1200
+ "</div>");
1201
+
11901202
CX("</div>");
11911203
CX("<div class='flex-container flex-column stretch'>");
11921204
CX("<textarea name='content' id='wikiedit-content-editor' "
11931205
"class='wikiedit' rows='25'>");
11941206
CX("</textarea>");
@@ -1213,16 +1225,19 @@
12131225
/* ^^^ fossil.page[methodName](content, callback) */
12141226
"data-f-preview-to='#wikiedit-tab-preview-wrapper' "
12151227
/* ^^^ dest elem ID */
12161228
">Refresh</button>");
12171229
/* Toggle auto-update of preview when the Preview tab is selected. */
1218
- style_labeled_checkbox("cb-preview-autoupdate",
1219
- NULL,
1220
- "Auto-refresh?",
1221
- "1", 1,
1222
- "If on, the preview will automatically "
1223
- "refresh when this tab is selected.");
1230
+ CX("<div class='input-with-label'>"
1231
+ "<input type='checkbox' value='1' "
1232
+ "id='cb-preview-autorefresh' checked>"
1233
+ "<label for='cb-preview-autorefresh'>Auto-refresh?</label>"
1234
+ "<div class='help-buttonlet'>"
1235
+ "If on, the preview will automatically "
1236
+ "refresh (if needed) when this tab is selected."
1237
+ "</div>"
1238
+ "</div>");
12241239
CX("<span class='save-button-slot'></span>");
12251240
CX("</div>"/*.wikiedit-options*/);
12261241
CX("<div id='wikiedit-tab-preview-wrapper'></div>");
12271242
CX("</div>"/*#wikiedit-tab-preview*/);
12281243
}
@@ -1270,13 +1285,16 @@
12701285
CX("<h2>Wiki Name Rules</h2>");
12711286
well_formed_wiki_name_rules();
12721287
CX("</div>"/*#wikiedit-tab-save*/);
12731288
}
12741289
1290
+ if(!builtin_bundle_all_fossil_js_apis()){
1291
+ builtin_emit_fossil_js_apis("fetch", "dom", "tabs", "confirmer",
1292
+ "storage", "popupwidget", 0);
1293
+ }
12751294
builtin_request_js("sbsdiff.js");
1276
- style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer",
1277
- "storage", "page.wikiedit", 0);
1295
+ builtin_request_js("fossil.page.wikiedit.js");
12781296
builtin_fulfill_js_requests();
12791297
/* Dynamically populate the editor... */
12801298
style_emit_script_tag(0,0);
12811299
{
12821300
/* Render the current page list to save us an XHR request
12831301
--- src/wiki.c
+++ src/wiki.c
@@ -1162,33 +1162,45 @@
1162 "data-tab-parent='wikiedit-tabs' "
1163 "data-tab-label='Editor' "
1164 "class='hidden'"
1165 ">");
1166 CX("<div class='flex-container flex-row child-gap-small'>");
1167 CX("<span class='input-with-label'>"
1168 "<label>Mime type</label>");
1169 mimetype_option_menu(0);
1170 CX("</span>");
1171 style_select_list_int("select-font-size",
1172 "editor_font_size", "Editor font size",
1173 NULL/*tooltip*/,
1174 100,
1175 "100%", 100, "125%", 125,
1176 "150%", 150, "175%", 175,
1177 "200%", 200, NULL);
1178 CX("<button class='wikiedit-save'>"
 
1179 "Save</button>"
1180 /*will get moved around dynamically*/);
1181 CX("<button class='wikiedit-save-close'>"
1182 "Save &amp; Close</button>"/*will get moved around dynamically*/);
 
 
 
 
 
1183 CX("<span class='save-button-slot'></span>");
1184 CX("<button class='wikiedit-content-reload' "
1185 "title='Reload the file from the server, discarding "
 
 
 
 
1186 "any local edits. To help avoid accidental loss of "
1187 "edits, it requires confirmation (a second click) within "
1188 "a few seconds or it will not reload.'"
1189 ">Discard &amp; Reload</button>");
 
 
1190 CX("</div>");
1191 CX("<div class='flex-container flex-column stretch'>");
1192 CX("<textarea name='content' id='wikiedit-content-editor' "
1193 "class='wikiedit' rows='25'>");
1194 CX("</textarea>");
@@ -1213,16 +1225,19 @@
1213 /* ^^^ fossil.page[methodName](content, callback) */
1214 "data-f-preview-to='#wikiedit-tab-preview-wrapper' "
1215 /* ^^^ dest elem ID */
1216 ">Refresh</button>");
1217 /* Toggle auto-update of preview when the Preview tab is selected. */
1218 style_labeled_checkbox("cb-preview-autoupdate",
1219 NULL,
1220 "Auto-refresh?",
1221 "1", 1,
1222 "If on, the preview will automatically "
1223 "refresh when this tab is selected.");
 
 
 
1224 CX("<span class='save-button-slot'></span>");
1225 CX("</div>"/*.wikiedit-options*/);
1226 CX("<div id='wikiedit-tab-preview-wrapper'></div>");
1227 CX("</div>"/*#wikiedit-tab-preview*/);
1228 }
@@ -1270,13 +1285,16 @@
1270 CX("<h2>Wiki Name Rules</h2>");
1271 well_formed_wiki_name_rules();
1272 CX("</div>"/*#wikiedit-tab-save*/);
1273 }
1274
 
 
 
 
1275 builtin_request_js("sbsdiff.js");
1276 style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer",
1277 "storage", "page.wikiedit", 0);
1278 builtin_fulfill_js_requests();
1279 /* Dynamically populate the editor... */
1280 style_emit_script_tag(0,0);
1281 {
1282 /* Render the current page list to save us an XHR request
1283
--- src/wiki.c
+++ src/wiki.c
@@ -1162,33 +1162,45 @@
1162 "data-tab-parent='wikiedit-tabs' "
1163 "data-tab-label='Editor' "
1164 "class='hidden'"
1165 ">");
1166 CX("<div class='flex-container flex-row child-gap-small'>");
1167 CX("<div class='input-with-label'>"
1168 "<label>Mime type</label>");
1169 mimetype_option_menu(0);
1170 CX("</div>");
1171 style_select_list_int("select-font-size",
1172 "editor_font_size", "Editor font size",
1173 NULL/*tooltip*/,
1174 100,
1175 "100%", 100, "125%", 125,
1176 "150%", 150, "175%", 175,
1177 "200%", 200, NULL);
1178 CX("<div class='input-with-label'>"
1179 "<button class='wikiedit-save'>"
1180 "Save</button>"
1181 "</div>" /*will get moved around dynamically*/);
1182 CX("<div class='input-with-label'>"
1183 "<button class='wikiedit-save-close'>"
1184 "Save &amp; Close</button>"
1185 "<div class='help-buttonlet'>"
1186 "Save edits to this page and returns to the wiki page viewer."
1187 "</div>"
1188 "</div>" /*will get moved around dynamically*/);
1189 CX("<span class='save-button-slot'></span>");
1190
1191 CX("<div class='input-with-label'>"
1192 "<button class='wikiedit-content-reload' "
1193 ">Discard &amp; Reload</button>"
1194 "<div class='help-buttonlet'>"
1195 "Reload the file from the server, discarding "
1196 "any local edits. To help avoid accidental loss of "
1197 "edits, it requires confirmation (a second click) within "
1198 "a few seconds or it will not reload."
1199 "</div>"
1200 "</div>");
1201
1202 CX("</div>");
1203 CX("<div class='flex-container flex-column stretch'>");
1204 CX("<textarea name='content' id='wikiedit-content-editor' "
1205 "class='wikiedit' rows='25'>");
1206 CX("</textarea>");
@@ -1213,16 +1225,19 @@
1225 /* ^^^ fossil.page[methodName](content, callback) */
1226 "data-f-preview-to='#wikiedit-tab-preview-wrapper' "
1227 /* ^^^ dest elem ID */
1228 ">Refresh</button>");
1229 /* Toggle auto-update of preview when the Preview tab is selected. */
1230 CX("<div class='input-with-label'>"
1231 "<input type='checkbox' value='1' "
1232 "id='cb-preview-autorefresh' checked>"
1233 "<label for='cb-preview-autorefresh'>Auto-refresh?</label>"
1234 "<div class='help-buttonlet'>"
1235 "If on, the preview will automatically "
1236 "refresh (if needed) when this tab is selected."
1237 "</div>"
1238 "</div>");
1239 CX("<span class='save-button-slot'></span>");
1240 CX("</div>"/*.wikiedit-options*/);
1241 CX("<div id='wikiedit-tab-preview-wrapper'></div>");
1242 CX("</div>"/*#wikiedit-tab-preview*/);
1243 }
@@ -1270,13 +1285,16 @@
1285 CX("<h2>Wiki Name Rules</h2>");
1286 well_formed_wiki_name_rules();
1287 CX("</div>"/*#wikiedit-tab-save*/);
1288 }
1289
1290 if(!builtin_bundle_all_fossil_js_apis()){
1291 builtin_emit_fossil_js_apis("fetch", "dom", "tabs", "confirmer",
1292 "storage", "popupwidget", 0);
1293 }
1294 builtin_request_js("sbsdiff.js");
1295 builtin_request_js("fossil.page.wikiedit.js");
 
1296 builtin_fulfill_js_requests();
1297 /* Dynamically populate the editor... */
1298 style_emit_script_tag(0,0);
1299 {
1300 /* Render the current page list to save us an XHR request
1301
--- www/javascript.md
+++ www/javascript.md
@@ -52,24 +52,24 @@
5252
rehash these same arguments on the [forum][ffor], we distill the common
5353
ones we’ve heard before and give our stock answers to them here:
5454
5555
1. “**It increases the size of the page download.**”
5656
57
- The heaviest such pages served by Fossil only have about 8 kB of
57
+ The heaviest such pages served by Fossil only have about 15 kB of
5858
compressed JavaScript. (You have to go out of your way to get Fossil
5959
to serve uncompressed pages.) This is negligible, even over very
6060
slow data connections. If you are still somehow on a 56 kbit/sec
6161
analog telephone modem, this extra script code would download in
62
- about a second.
62
+ a few seconds.
6363
6464
Most JavaScript-based Fossil pages use less code than that.
6565
66
- Atop that, Fossil 2.12 adds new script delivery methods with
67
- aggressive caching enabled so that typical page loads will skip
66
+ Atop that, Fossil sends HTTP headers to the browser that allow it
67
+ to perform aggressive caching so that typical page loads will skip
6868
re-loading this content on subsequent loads. These features are
6969
currently optional: you must either set the new [`fossil server
70
- --jsmode` option][fsrv] or the corresponding `jsmode` control line
70
+ --jsmode bundle` option][fsrv] or the corresponding `jsmode` control line
7171
in your [`fossil cgi`][fcgi] script when setting up your
7272
[Fossil server][fshome]. That done, Fossil’s JavaScript files will
7373
load almost instantly from the browser’s cache after the initial
7474
page load, rather than be re-transferred over the network.
7575
7676
--- www/javascript.md
+++ www/javascript.md
@@ -52,24 +52,24 @@
52 rehash these same arguments on the [forum][ffor], we distill the common
53 ones we’ve heard before and give our stock answers to them here:
54
55 1. “**It increases the size of the page download.**”
56
57 The heaviest such pages served by Fossil only have about 8 kB of
58 compressed JavaScript. (You have to go out of your way to get Fossil
59 to serve uncompressed pages.) This is negligible, even over very
60 slow data connections. If you are still somehow on a 56 kbit/sec
61 analog telephone modem, this extra script code would download in
62 about a second.
63
64 Most JavaScript-based Fossil pages use less code than that.
65
66 Atop that, Fossil 2.12 adds new script delivery methods with
67 aggressive caching enabled so that typical page loads will skip
68 re-loading this content on subsequent loads. These features are
69 currently optional: you must either set the new [`fossil server
70 --jsmode` option][fsrv] or the corresponding `jsmode` control line
71 in your [`fossil cgi`][fcgi] script when setting up your
72 [Fossil server][fshome]. That done, Fossil’s JavaScript files will
73 load almost instantly from the browser’s cache after the initial
74 page load, rather than be re-transferred over the network.
75
76
--- www/javascript.md
+++ www/javascript.md
@@ -52,24 +52,24 @@
52 rehash these same arguments on the [forum][ffor], we distill the common
53 ones we’ve heard before and give our stock answers to them here:
54
55 1. “**It increases the size of the page download.**”
56
57 The heaviest such pages served by Fossil only have about 15 kB of
58 compressed JavaScript. (You have to go out of your way to get Fossil
59 to serve uncompressed pages.) This is negligible, even over very
60 slow data connections. If you are still somehow on a 56 kbit/sec
61 analog telephone modem, this extra script code would download in
62 a few seconds.
63
64 Most JavaScript-based Fossil pages use less code than that.
65
66 Atop that, Fossil sends HTTP headers to the browser that allow it
67 to perform aggressive caching so that typical page loads will skip
68 re-loading this content on subsequent loads. These features are
69 currently optional: you must either set the new [`fossil server
70 --jsmode bundle` option][fsrv] or the corresponding `jsmode` control line
71 in your [`fossil cgi`][fcgi] script when setting up your
72 [Fossil server][fshome]. That done, Fossil’s JavaScript files will
73 load almost instantly from the browser’s cache after the initial
74 page load, rather than be re-transferred over the network.
75
76

Keyboard Shortcuts

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