Fossil

Check-in [a4b73fd081]
Login

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

Overview
Comment:/wikiedit and /fileedit now dump all of their common static JS into a single SCRIPT tag instead of making numerous requests, per forum feedback. Fixed/expanded some of the newer script-emitting logic in style.c to handle these. wikiedit no longer mangles the page-level header.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | ajax-wiki-editor
Files: files | file ages | folders
SHA3-256: a4b73fd081e9bb45d57da293693ca70d25d5a4962985879e63066b328877e00e
User & Date: stephan 2020-07-31 14:44:49.006
Context
2020-07-31
16:14
Extended /builtin/filename to support /builtin/:filename for packing up cacheable bundles of page-specific code. /wikiedit, /fileedit, and /forumpost now serve their JS that way. ... (check-in: 18da05b903 user: stephan tags: ajax-wiki-editor)
14:44
/wikiedit and /fileedit now dump all of their common static JS into a single SCRIPT tag instead of making numerous requests, per forum feedback. Fixed/expanded some of the newer script-emitting logic in style.c to handle these. wikiedit no longer mangles the page-level header. ... (check-in: a4b73fd081 user: stephan tags: ajax-wiki-editor)
12:35
Minor style tweaks after trying the editor in different skins. ... (check-in: 7418144b3f user: stephan tags: ajax-wiki-editor)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/fileedit.c.
1478
1479
1480
1481
1482
1483
1484

























1485
1486
1487
1488
1489
1490
1491
  db_end_transaction(0/*noting that dry-run mode will have already
                      ** set this to rollback mode. */);
  fossil_free(zNewUuid);
  blob_reset(&err);
  blob_reset(&manifest);
  CheckinMiniInfo_cleanup(&cimi);
}


























/*
** WEBPAGE: fileedit
**
** Enables the online editing and committing of individual text files.
** Requires that the user have Write permissions.
**







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







1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
  db_end_transaction(0/*noting that dry-run mode will have already
                      ** set this to rollback mode. */);
  fossil_free(zNewUuid);
  blob_reset(&err);
  blob_reset(&manifest);
  CheckinMiniInfo_cleanup(&cimi);
}

/*
** Emits all of the "core" static JS needed by /fileedit into a single
** SCRIPT tag.
**
** TODO: a common mechanism which will let us bundle this type of
** blob into a single cacheable request.
*/
static void fileedit_emit_js(void){
  style_emit_script_tag(0,0);
  style_emit_script_fossil_bootstrap(1);
  style_emit_script_builtin(1,0,"sbsdiff.js");
  style_emit_script_fetch(1,0);
  style_emit_script_tabs(1,0)/*also emits fossil.dom*/;
  style_emit_script_confirmer(1,0);
  style_emit_script_builtin(1, 0, "fossil.storage.js");
  /*
  ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is
  ** used for dynamically toggling certain UI components on and off.
  ** Must come before fossil.page.fileedit.js.
  */
  ajax_emit_js_preview_modes(0);
  style_emit_script_builtin(1, 0, "fossil.page.fileedit.js");
  style_emit_script_tag(1,0);
}

/*
** WEBPAGE: fileedit
**
** Enables the online editing and committing of individual text files.
** Requires that the user have Write permissions.
**
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
      blob_appendf(&endScript,"}");
    }
    blob_appendf(&endScript,");\n");
  }

  blob_reset(&err);
  CheckinMiniInfo_cleanup(&cimi);
  style_emit_script_fossil_bootstrap(0);
  append_diff_javascript(1);
  style_emit_script_fetch(0);
  style_emit_script_tabs(0)/*also emits fossil.dom*/;
  style_emit_script_confirmer(0);
  style_emit_script_builtin(0, "fossil.storage.js");

  /*
  ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is
  ** used for dynamically toggling certain UI components on and off.
  ** Must come before fossil.page.fileedit.js.
  */
  ajax_emit_js_preview_modes(1);

  style_emit_script_builtin(0, "fossil.page.fileedit.js");
  if(blob_size(&endScript)>0){
    style_emit_script_tag(0,0);
    CX("\n(function(){\n");
    CX("try{\n%b}\n"
       "catch(e){"
       "fossil.error(e); console.error('Exception:',e);"
       "}\n",







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







1972
1973
1974
1975
1976
1977
1978






1979








1980
1981
1982
1983
1984
1985
1986
      blob_appendf(&endScript,"}");
    }
    blob_appendf(&endScript,");\n");
  }

  blob_reset(&err);
  CheckinMiniInfo_cleanup(&cimi);






  fileedit_emit_js();








  if(blob_size(&endScript)>0){
    style_emit_script_tag(0,0);
    CX("\n(function(){\n");
    CX("try{\n%b}\n"
       "catch(e){"
       "fossil.error(e); console.error('Exception:',e);"
       "}\n",
Changes to src/forum.c.
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
*/
static void forum_emit_page_js(){
  static int once = 0;
  if(0==once){
    once = 1;
    style_load_js("forum.js");
    style_emit_script_fossil_bootstrap(0);
    style_emit_script_dom(0);
    style_emit_script_builtin(0, "fossil.page.forumpost.js");
  }
}

/*
** WEBPAGE: forumpost
**
** Show a single forum posting. The posting is shown in context with







|
|







759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
*/
static void forum_emit_page_js(){
  static int once = 0;
  if(0==once){
    once = 1;
    style_load_js("forum.js");
    style_emit_script_fossil_bootstrap(0);
    style_emit_script_dom(0, 1);
    style_emit_script_builtin(0, 1, "fossil.page.forumpost.js");
  }
}

/*
** WEBPAGE: forumpost
**
** Show a single forum posting. The posting is shown in context with
Changes to src/fossil.page.wikiedit.js.
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
  /**
     Update the page title and header based on the state of
     this.winfo. A no-op if this.winfo is not set. Returns this.
  */
  P.updatePageTitle = function f(){
    if(!f.titleElement){
      f.titleElement = document.head.querySelector('title');
      f.pageTitleHeader = document.querySelector('div.header .title');
    }
    var title = ['Wiki Editor:'];
    const wi = P.winfo;
    if(wi){
      if(!wi.version && 'sandbox'!==wi.type) title.push(P.config.editStateMarkers.isNew);
      else if($stash.getWinfo(wi)) title.push(P.config.editStateMarkers.isModified)
      title.push(wi.name);
    }else{
      title.push('(no page loaded)');
    }
    title = title.join(' ');
    f.titleElement.innerText = title;
    f.pageTitleHeader.innerText = title;
    return this;
  };

  /**
     Change the save button depending on whether we have stuff to save
     or not.
  */







|

|








|
|
<







795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814

815
816
817
818
819
820
821
  /**
     Update the page title and header based on the state of
     this.winfo. A no-op if this.winfo is not set. Returns this.
  */
  P.updatePageTitle = function f(){
    if(!f.titleElement){
      f.titleElement = document.head.querySelector('title');
      f.pageTitleHeader = document.querySelector('#wikiedit-page-name > span');
    }
    const title = ['Wiki Editor:'];
    const wi = P.winfo;
    if(wi){
      if(!wi.version && 'sandbox'!==wi.type) title.push(P.config.editStateMarkers.isNew);
      else if($stash.getWinfo(wi)) title.push(P.config.editStateMarkers.isModified)
      title.push(wi.name);
    }else{
      title.push('(no page loaded)');
    }
    f.pageTitleHeader.innerText = title[1];
    f.titleElement.innerText = title.join(' ');

    return this;
  };

  /**
     Change the save button depending on whether we have stuff to save
     or not.
  */
Changes to src/style.c.
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543



1544
1545
1546

1547
1548
1549
1550
1551
1552

1553

1554
1555
1556
1557
1558
1559
1560
/*
** The first time this is called, it emits code to install and
** bootstrap the window.fossil object, using the built-in file
** fossil.bootstrap.js (not to be confused with bootstrap.js).
**
** Subsequent calls are no-ops.
**
** If passed a true value, it emits the contents directly to the page
** output, else it emits a script tag with a src=builtin/... to load
** the script. It always outputs a small pre-bootstrap element in its
** own script tag to initialize parts which need C-runtime-level



** information, before loading the main fossil.bootstrap.js either
** inline or via a <script src=...>, as specified by the first
** argument.

*/
void style_emit_script_fossil_bootstrap(int asInline){
  static int once = 0;
  if(0==once++){
    /* Set up the generic/app-agnostic parts of window.fossil
    ** which require C-level state... */

    style_emit_script_tag(0,0);

    CX("(function(){\n"
       "if(!window.fossil) window.fossil={};\n"
       "window.fossil.version = %!j;\n"
    /* fossil.rootPath is the top-most CGI/server path,
    ** including a trailing slash. */
       "window.fossil.rootPath = %!j+'/';\n",
       get_version(), g.zTop);







|
|
|
|
>
>
>
|
|
|
>






>
|
>







1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
/*
** The first time this is called, it emits code to install and
** bootstrap the window.fossil object, using the built-in file
** fossil.bootstrap.js (not to be confused with bootstrap.js).
**
** Subsequent calls are no-ops.
**
** It emits 2 parts:
**
** 1) window.fossil core object, some of which depends on C-lelel
** runtime data. That part of the script is always emitted inline. If
** addScripTag is true then it is wrapped in its own SCRIPT tag, else
** it is assumed that the caller already opened a tag.
**
** 2) Emits the static fossil.bootstrap.js. If asInline is true then
** it is emitted inline with the components from (1), else it is
** emitted as a separate SCRIPT tag with
** src=/builtin/fossil.bootstrap.js (so causes another HTTP request).
*/
void style_emit_script_fossil_bootstrap(int asInline){
  static int once = 0;
  if(0==once++){
    /* Set up the generic/app-agnostic parts of window.fossil
    ** which require C-level state... */
    if(asInline==0){
      style_emit_script_tag(0,0);
    }
    CX("(function(){\n"
       "if(!window.fossil) window.fossil={};\n"
       "window.fossil.version = %!j;\n"
    /* fossil.rootPath is the top-most CGI/server path,
    ** including a trailing slash. */
       "window.fossil.rootPath = %!j+'/';\n",
       get_version(), g.zTop);
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
    */
    CX("window.fossil.page = {"
       "name:\"%T\""
       "};\n", g.zPath);
    CX("})();\n");
    /* The remaining fossil object bootstrap code is not dependent on
    ** C-runtime state... */
    if(asInline){
      CX("%s\n", builtin_text("fossil.bootstrap.js"));
    }
    style_emit_script_tag(1,0);
    if(asInline==0){
      style_emit_script_builtin(0, "fossil.bootstrap.js");
    }
  }
}

/*
** If passed 0 as its first argument, it emits a script opener tag
** with this request's nonce. If passed non-0 it emits a script







|

|
|
<
|







1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593

1594
1595
1596
1597
1598
1599
1600
1601
    */
    CX("window.fossil.page = {"
       "name:\"%T\""
       "};\n", g.zPath);
    CX("})();\n");
    /* The remaining fossil object bootstrap code is not dependent on
    ** C-runtime state... */
    if(asInline!=0){
      CX("%s\n", builtin_text("fossil.bootstrap.js"));
    }else{
      style_emit_script_tag(1,0);

      style_emit_script_builtin(0,1,"fossil.bootstrap.js");
    }
  }
}

/*
** If passed 0 as its first argument, it emits a script opener tag
** with this request's nonce. If passed non-0 it emits a script
1620
1621
1622
1623
1624
1625
1626
1627

1628


1629
1630
1631
1632
1633
1634

1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654




1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673




1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
    CX("</script>\n");
  }
}

/*
** Emits a script tag which uses content from a builtin script file.
**
** If asInline is true, it is emitted directly as an opening tag, the

** content of the zName builtin file, and a closing tag.


**
** If it is false, a script tag loading it via
** src=builtin/{{zName}}?cache=XYZ is emitted, where XYZ is a
** build-time-dependent cache-buster value.
*/
void style_emit_script_builtin(int asInline, char const * zName){

  if(asInline){
    style_emit_script_tag(0,0);
    CX("%s", builtin_text(zName));
    style_emit_script_tag(1,0);
  }else{
    char * zFullName = mprintf("builtin/%s",zName);
    const char * zHash = fossil_exe_id();
    CX("<script src='%R/%T?cache=%.8s'></script>\n",
       zFullName, zHash);
    fossil_free(zFullName);
  }
}

/*
** The first time this is called it emits the JS code from the
** built-in file fossil.fossil.js. Subsequent calls are no-ops.
**
** If passed a true value, it emits the contents directly
** to the page output, else it emits a script tag with a
** src=builtin/... to load the script.




**
** Note that this code relies on that loaded via
** style_emit_script_fossil_bootstrap() but it does not call that
** routine.
*/
void style_emit_script_fetch(int asInline){
  static int once = 0;
  if(0==once++){
    style_emit_script_builtin(asInline, "fossil.fetch.js");
  }
}

/*
** The first time this is called it emits the JS code from the
** built-in file fossil.dom.js. Subsequent calls are no-ops.
**
** If passed a true value, it emits the contents directly
** to the page output, else it emits a script tag with a
** src=builtin/... to load the script.




**
** Note that this code relies on that loaded via
** style_emit_script_fossil_bootstrap(), but it does not call that
** routine.
*/
void style_emit_script_dom(int asInline){
  static int once = 0;
  if(0==once++){
    style_emit_script_builtin(asInline, "fossil.dom.js");
  }
}

/*
** The first time this is called, it calls style_emit_script_dom(),
** passing it the given asInline value, and emits the JS code from the
** built-in file fossil.tabs.js. Subsequent calls are no-ops.
**
** If passed a true value, it emits the contents directly
** to the page output, else it emits a script tag with a
** src=builtin/... to load the script.
*/
void style_emit_script_tabs(int asInline){
  static int once = 0;
  if(0==once++){
    style_emit_script_dom(asInline);
    style_emit_script_builtin(asInline, "fossil.tabs.js");
  }
}

/*
** The first time this is called it emits the JS code from the
** built-in file fossil.confirmer.js. Subsequent calls are no-ops.
**
** If passed a true value, it emits the contents directly
** to the page output, else it emits a script tag with a
** src=builtin/... to load the script.
*/
void style_emit_script_confirmer(int asInline){
  static int once = 0;
  if(0==once++){
    style_emit_script_builtin(asInline, "fossil.confirmer.js");
  }
}







|
>
|
>
>





|
>

<

<













|


>
>
>
>





|


|







|


>
>
>
>





|


|




|
<
|
<
<
<
<

|


|
|




<
|
<
<
<
<

|


|


1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644

1645

1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702

1703




1704
1705
1706
1707
1708
1709
1710
1711
1712
1713

1714




1715
1716
1717
1718
1719
1720
1721
    CX("</script>\n");
  }
}

/*
** Emits a script tag which uses content from a builtin script file.
**
** If asInline is false, the script is emitted as a SCRIPT tag with a
** src attribute of /builtin/zName and the 2nd parameter is
** ignored. If asInline is true then the contents of the script are
** emitted directly, with a wrapping SCRIPT tag if addScripTag is
** true, else no wrapping script tag..
**
** If it is false, a script tag loading it via
** src=builtin/{{zName}}?cache=XYZ is emitted, where XYZ is a
** build-time-dependent cache-buster value.
*/
void style_emit_script_builtin(int asInline, int addScripTag,
                               char const * zName){
  if(asInline){

    CX("%s", builtin_text(zName));

  }else{
    char * zFullName = mprintf("builtin/%s",zName);
    const char * zHash = fossil_exe_id();
    CX("<script src='%R/%T?cache=%.8s'></script>\n",
       zFullName, zHash);
    fossil_free(zFullName);
  }
}

/*
** The first time this is called it emits the JS code from the
** built-in file fossil.fossil.js. Subsequent calls are no-ops.
**
** If passed a true first argument, it emits the contents directly
** to the page output, else it emits a script tag with a
** src=builtin/... to load the script.
**
** If asInline is true and addScripTag is true then the contents
** are emitted directly but wrapped in a SCRIPT tag. If asInline
** is false, addScriptTag is ignored.
**
** Note that this code relies on that loaded via
** style_emit_script_fossil_bootstrap() but it does not call that
** routine.
*/
void style_emit_script_fetch(int asInline, int addScripTag){
  static int once = 0;
  if(0==once++){
    style_emit_script_builtin(asInline, addScripTag, "fossil.fetch.js");
  }
}

/*
** The first time this is called it emits the JS code from the
** built-in file fossil.dom.js. Subsequent calls are no-ops.
**
** If passed a true first argument, it emits the contents directly
** to the page output, else it emits a script tag with a
** src=builtin/... to load the script.
**
** If asInline is true and addScripTag is true then the contents
** are emitted directly but wrapped in a SCRIPT tag. If asInline
** is false, addScriptTag is ignored.
**
** Note that this code relies on that loaded via
** style_emit_script_fossil_bootstrap(), but it does not call that
** routine.
*/
void style_emit_script_dom(int asInline, int addScripTag){
  static int once = 0;
  if(0==once++){
    style_emit_script_builtin(asInline, addScripTag, "fossil.dom.js");
  }
}

/*
** The fossil.tabs.js counterpart of style_emit_script_fetch().

** Also emits fossil.dom.js.




*/
void style_emit_script_tabs(int asInline, int addScripTag){
  static int once = 0;
  if(0==once++){
    style_emit_script_dom(asInline, addScripTag);
    style_emit_script_builtin(asInline, addScripTag, "fossil.tabs.js");
  }
}

/*

** The fossil.confirmer.js counterpart of style_emit_script_fetch().




*/
void style_emit_script_confirmer(int asInline, int addScripTag){
  static int once = 0;
  if(0==once++){
    style_emit_script_builtin(asInline, 0, "fossil.confirmer.js");
  }
}
Changes to src/style.wikiedit.css.
114
115
116
117
118
119
120





body.wikiedit span.save-button-slot {
  /* These invisible placeholders mark spots in the UI
     (max. 1 per tab) to where the save button gets
     relocated as we switch between tabs. */
  display: none;
}











>
>
>
>
114
115
116
117
118
119
120
121
122
123
124

body.wikiedit span.save-button-slot {
  /* These invisible placeholders mark spots in the UI
     (max. 1 per tab) to where the save button gets
     relocated as we switch between tabs. */
  display: none;
}

body.wikiedit #wikiedit-page-name > span {
  font-family: monospace;
}
Changes to src/wiki.c.
1056
1057
1058
1059
1060
1061
1062



















1063
1064
1065
1066
1067
1068
1069
                     "CSRF violation (make sure sending of HTTP "
                     "Referer headers is enabled for XHR "
                     "connections).");
    return;
  }
  pRoute->xCallback();
}




















/*
** WEBPAGE: wikiedit
** URL: /wikedit?name=PAGENAME
**
** The main front-end for the Ajax-based wiki editor app. Passing
** in the name of an unknown page will trigger the creation







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







1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
                     "CSRF violation (make sure sending of HTTP "
                     "Referer headers is enabled for XHR "
                     "connections).");
    return;
  }
  pRoute->xCallback();
}

/*
** Emits all of the "core" static JS needed by /wikiedit into a single
** SCRIPT tag.
**
** TODO: a common mechanism which will let us bundle this type of
** blob into a single cacheable request.
*/
static void wikiedit_emit_js(void){
  style_emit_script_tag(0,0);
  style_emit_script_fossil_bootstrap(1);
  style_emit_script_builtin(1,0,"sbsdiff.js");
  style_emit_script_fetch(1, 0);
  style_emit_script_tabs(1,0)/*also emits fossil.dom*/;
  style_emit_script_confirmer(1,0);
  style_emit_script_builtin(1, 0, "fossil.storage.js");
  style_emit_script_builtin(1, 0, "fossil.page.wikiedit.js");
  style_emit_script_tag(1,0);
}

/*
** WEBPAGE: wikiedit
** URL: /wikedit?name=PAGENAME
**
** The main front-end for the Ajax-based wiki editor app. Passing
** in the name of an unknown page will trigger the creation
1108
1109
1110
1111
1112
1113
1114


1115
1116
1117
1118
1119
1120
1121
  style_header("Wiki Editor");

  /* Status bar */
  CX("<div id='fossil-status-bar' "
     "title='Status message area. Double-click to clear them.'>"
     "Status messages will go here.</div>\n"
     /* will be moved into the tab container via JS */);


  
  /* Main tab container... */
  CX("<div id='wikiedit-tabs' class='tab-container'>Loading...</div>");
  /* The .hidden class on the following tab elements is to help lessen
     the FOUC effect of the tabs before JS re-assembles them. */

  /******* Page list *******/







>
>







1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
  style_header("Wiki Editor");

  /* Status bar */
  CX("<div id='fossil-status-bar' "
     "title='Status message area. Double-click to clear them.'>"
     "Status messages will go here.</div>\n"
     /* will be moved into the tab container via JS */);

  CX("<div id='wikiedit-page-name'>Editing: <span>(no file loaded)</span></div>");
  
  /* Main tab container... */
  CX("<div id='wikiedit-tabs' class='tab-container'>Loading...</div>");
  /* The .hidden class on the following tab elements is to help lessen
     the FOUC effect of the tabs before JS re-assembles them. */

  /******* Page list *******/
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
       "without having to actually save anything, nor pollute "
       "the repo with endless test runs. Any attempt to save the "
       "sandbox page will fail.</p>");
    CX("<hr><h3>Wiki Name Rules</h3>");
    well_formed_wiki_name_rules();
    CX("</div>"/*#wikiedit-tab-save*/);
  }
  
  style_emit_script_fossil_bootstrap(0);
  append_diff_javascript(1);
  style_emit_script_fetch(0);
  style_emit_script_tabs(0)/*also emits fossil.dom*/;
  style_emit_script_confirmer(0);
  style_emit_script_builtin(0, "fossil.storage.js");
  style_emit_script_builtin(0, "fossil.page.wikiedit.js");

  /* Dynamically populate the editor... */
  style_emit_script_tag(0,0);
  CX("\nfossil.onPageLoad(function(){\n");
  CX("const P = fossil.page;\n"
     "try{\n");
  if(found){







|
<
<
|
<
<
<
<







1260
1261
1262
1263
1264
1265
1266
1267


1268




1269
1270
1271
1272
1273
1274
1275
       "without having to actually save anything, nor pollute "
       "the repo with endless test runs. Any attempt to save the "
       "sandbox page will fail.</p>");
    CX("<hr><h3>Wiki Name Rules</h3>");
    well_formed_wiki_name_rules();
    CX("</div>"/*#wikiedit-tab-save*/);
  }



  wikiedit_emit_js();





  /* Dynamically populate the editor... */
  style_emit_script_tag(0,0);
  CX("\nfossil.onPageLoad(function(){\n");
  CX("const P = fossil.page;\n"
     "try{\n");
  if(found){