Fossil SCM

Numerous minor cleanups.

stephan 2020-05-05 08:41 fileedit-ajaxify
Commit f54ac21745eb59e628cfd19bccc5f3c303e1d4f6a4c0712d6352374a21a91c02
--- src/default_css.txt
+++ src/default_css.txt
@@ -969,5 +969,20 @@
969969
vertical-align: middle;
970970
}
971971
.hidden {
972972
display: none;
973973
}
974
+.font-size-100 {
975
+ font-size: 100%;
976
+}
977
+.font-size-125 {
978
+ font-size: 125%;
979
+}
980
+.font-size-150 {
981
+ font-size: 150%;
982
+}
983
+.font-size-175 {
984
+ font-size: 175%;
985
+}
986
+.font-size-200 {
987
+ font-size: 200%;
988
+}
974989
--- src/default_css.txt
+++ src/default_css.txt
@@ -969,5 +969,20 @@
969 vertical-align: middle;
970 }
971 .hidden {
972 display: none;
973 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
974
--- src/default_css.txt
+++ src/default_css.txt
@@ -969,5 +969,20 @@
969 vertical-align: middle;
970 }
971 .hidden {
972 display: none;
973 }
974 .font-size-100 {
975 font-size: 100%;
976 }
977 .font-size-125 {
978 font-size: 125%;
979 }
980 .font-size-150 {
981 font-size: 150%;
982 }
983 .font-size-175 {
984 font-size: 175%;
985 }
986 .font-size-200 {
987 font-size: 200%;
988 }
989
+63 -51
--- src/fileedit.c
+++ src/fileedit.c
@@ -903,11 +903,11 @@
903903
904904
enum fileedit_render_preview_flags {
905905
FE_PREVIEW_LINE_NUMBERS = 1
906906
};
907907
enum fileedit_render_modes {
908
-/* GUESS must be 0. All others have specified values. */
908
+/* GUESS must be 0. All others have unspecified values. */
909909
FE_RENDER_GUESS = 0,
910910
FE_RENDER_PLAIN_TEXT,
911911
FE_RENDER_HTML,
912912
FE_RENDER_WIKI
913913
};
@@ -1015,11 +1015,11 @@
10151015
db_finalize(&stmt);
10161016
return zFileUuid;
10171017
}
10181018
10191019
/*
1020
-** Helper for /filepage_xyz routes. Clears the CGI content buffer,
1020
+** Helper for /fileedit_xyz routes. Clears the CGI content buffer,
10211021
** sets an error status code, and queues up a JSON response in the
10221022
** form of an object:
10231023
**
10241024
** {error: formatted message}
10251025
**
@@ -1135,10 +1135,11 @@
11351135
const char * zFilename = PD("file",P("name"));
11361136
const char * zRev = P("r");
11371137
int vid, frid;
11381138
Blob content = empty_blob;
11391139
const char * zMime;
1140
+
11401141
if(!fileedit_ajax_boostrap()
11411142
|| !fileedit_ajax_setup_filerev(zRev, 0, &vid,
11421143
zFilename, &frid)){
11431144
return;
11441145
}
@@ -1149,15 +1150,24 @@
11491150
}
11501151
11511152
/*
11521153
** WEBPAGE: fileedit_preview
11531154
**
1154
-** Query parameters:
1155
+** Required query parameters:
11551156
**
11561157
** file=FILENAME
1157
-** render_mode=integer (FE_RENDER_xxx) (default=FE_RENDER_GUESS)
11581158
** content=text
1159
+**
1160
+** Optional query parameters:
1161
+**
1162
+** render_mode=integer (FE_RENDER_xxx) (default=FE_RENDER_GUESS)
1163
+**
1164
+** ln=0 or 1 to disable/enable line number mode in
1165
+** FE_RENDER_PLAIN_TEXT mode.
1166
+**
1167
+** iframe_height=integer (default=40) Height, in EMs of HTML preview
1168
+** iframe.
11591169
**
11601170
** User must have Write access to use this page.
11611171
**
11621172
** Responds with the HTML content of the preview. On error it produces
11631173
** a JSON response as documented for fileedit_ajax_error().
@@ -1167,10 +1177,11 @@
11671177
const char * zContent = P("content");
11681178
int renderMode = atoi(PD("render_mode","0"));
11691179
int ln = atoi(PD("ln","0"));
11701180
int iframeHeight = atoi(PD("iframe_height","40"));
11711181
Blob content = empty_blob;
1182
+
11721183
if(!fileedit_ajax_boostrap()
11731184
|| !fileedit_ajax_check_filename(zFilename)){
11741185
return;
11751186
}
11761187
cgi_set_content_type("text/html");
@@ -1181,15 +1192,20 @@
11811192
}
11821193
11831194
/*
11841195
** WEBPAGE: fileedit_diff
11851196
**
1197
+** Required query parameters:
1198
+**
11861199
** file=FILENAME
1187
-** sbs=integer (1=side-by-side or 0=unified)
11881200
** content=text
11891201
** r=checkin version
11901202
**
1203
+** Optional parameters:
1204
+**
1205
+** sbs=integer (1=side-by-side or 0=unified, default=0)
1206
+**
11911207
** User must have Write access to use this page.
11921208
**
11931209
** Responds with the HTML content of the diff. On error it produces a
11941210
** JSON response as documented for fileedit_ajax_error().
11951211
*/
@@ -1205,10 +1221,11 @@
12051221
const char * zContent = P("content");
12061222
char * zRevUuid = 0;
12071223
int isSbs = atoi(PD("sbs","0"));
12081224
int vid, frid;
12091225
Blob content = empty_blob;
1226
+
12101227
if(!fileedit_ajax_boostrap()
12111228
|| !fileedit_ajax_setup_filerev(zRev, &zRevUuid, &vid,
12121229
zFilename, &frid)){
12131230
return;
12141231
}
@@ -1221,25 +1238,27 @@
12211238
fossil_free(zRevUuid);
12221239
blob_reset(&content);
12231240
}
12241241
12251242
/*
1226
-** Sets up and validates must, but not all, of p's checkin-related
1243
+** Sets up and validates most, but not all, of p's checkin-related
12271244
** state from the CGI environment. Returns 0 on success or a suggested
12281245
** HTTP result code on error, in which case a message will have been
12291246
** written to pErr.
12301247
**
12311248
** It always fails if it cannot completely resolve the 'file' and 'r'
12321249
** parameters, including verifying that the refer to a real
1233
-** file/version combination. Most others are optional.
1250
+** file/version combination and editable by the current user. All
1251
+** others are optional (at this level, anyway, but upstream code might
1252
+** require them).
12341253
**
12351254
** Intended to be used only by /filepage and /filepage_commit.
12361255
*/
12371256
static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr){
1238
- char * zFileUuid = 0;
1239
- const char * zFlag;
1240
- int rc = 0, vid = 0, frid = 0;
1257
+ char * zFileUuid = 0; /* UUID of file content */
1258
+ const char * zFlag; /* generic flag */
1259
+ int rc = 0, vid = 0, frid = 0; /* result code, checkin/file rids */
12411260
12421261
#define fail(EXPR) blob_appendf EXPR; goto end_fail
12431262
zFlag = PD("file",P("name"));
12441263
if(zFlag==0 || !*zFlag){
12451264
rc = 400;
@@ -1301,11 +1320,10 @@
13011320
zFlag = P("comment_mimetype");
13021321
if(zFlag){
13031322
p->zCommentMimetype = mprintf("%s",zFlag);
13041323
zFlag = 0;
13051324
}
1306
- p->zUser = mprintf("%s",g.zLogin);
13071325
#define p_int(K) atoi(PD(K,"0"))
13081326
if(p_int("dry_run")!=0){
13091327
p->flags |= CIMINI_DRY_RUN;
13101328
}
13111329
if(p_int("allow_fork")!=0){
@@ -1323,23 +1341,21 @@
13231341
if(p_int("prefer_delta")!=0){
13241342
p->flags |= CIMINI_PREFER_DELTA;
13251343
}
13261344
13271345
/* EOL conversion policy... */
1328
- {
1329
- const int eolMode = p_int("eol");
1330
- switch(eolMode){
1331
- case 1: p->flags |= CIMINI_CONVERT_EOL_UNIX; break;
1332
- case 2: p->flags |= CIMINI_CONVERT_EOL_WINDOWS; break;
1333
- default: p->flags |= CIMINI_CONVERT_EOL_INHERIT; break;
1334
- }
1346
+ switch(p_int("eol")){
1347
+ case 1: p->flags |= CIMINI_CONVERT_EOL_UNIX; break;
1348
+ case 2: p->flags |= CIMINI_CONVERT_EOL_WINDOWS; break;
1349
+ default: p->flags |= CIMINI_CONVERT_EOL_INHERIT; break;
13351350
}
13361351
#undef p_int
13371352
/*
13381353
** TODO?: date-override date selection field. Maybe use
13391354
** an input[type=datetime-local].
13401355
*/
1356
+ p->zUser = mprintf("%s",g.zLogin);
13411357
return 0;
13421358
end_fail:
13431359
#undef fail
13441360
fossil_free(zFileUuid);
13451361
return rc ? rc : 500;
@@ -1363,25 +1379,26 @@
13631379
**
13641380
** User must have Write access to use this page.
13651381
**
13661382
** Responds with JSON:
13671383
**
1368
-** {uuid: newUUID,
1384
+** {
1385
+** uuid: newUUID,
13691386
** manifest: text of manifest,
13701387
** dryRun: bool
13711388
** }
13721389
**
13731390
** On error it produces a JSON response as documented for
13741391
** fileedit_ajax_error().
13751392
*/
13761393
void fileedit_ajax_commit(){
1377
- int newVid = 0;
1378
- Blob err = empty_blob;
1379
- Blob manifest = empty_blob;
1380
- CheckinMiniInfo cimi;
1381
- int rc;
1382
- char * zNewUuid = 0;
1394
+ Blob err = empty_blob; /* Error messages */
1395
+ Blob manifest = empty_blob; /* raw new manifest */
1396
+ CheckinMiniInfo cimi; /* checkin state */
1397
+ int rc; /* generic result code */
1398
+ int newVid = 0; /* new version's RID */
1399
+ char * zNewUuid = 0; /* newVid's UUID */
13831400
13841401
if(!fileedit_ajax_boostrap()){
13851402
goto end_cleanup;
13861403
}
13871404
db_begin_transaction();
@@ -1408,11 +1425,12 @@
14081425
CX("\"uuid\":\"%j\",", zNewUuid);
14091426
CX("\"dryRun\": %s,",
14101427
(CIMINI_DRY_RUN & cimi.flags) ? "true" : "false");
14111428
CX("\"manifest\": \"%j\"", blob_str(&manifest));
14121429
CX("}");
1413
- db_end_transaction(0);
1430
+ db_end_transaction(0/*noting that dry-run mode will have already
1431
+ ** set this to rollback mode. */);
14141432
end_cleanup:
14151433
fossil_free(zNewUuid);
14161434
blob_reset(&err);
14171435
blob_reset(&manifest);
14181436
CheckinMiniInfo_cleanup(&cimi);
@@ -1453,19 +1471,17 @@
14531471
CheckinMiniInfo cimi; /* Checkin state */
14541472
int previewHtmlHeight = 0; /* iframe height (EMs) */
14551473
int previewRenderMode = FE_RENDER_GUESS; /* preview mode */
14561474
char * zFileUuid = 0; /* File content UUID */
14571475
Blob err = empty_blob; /* Error report */
1458
- Blob submitResult = empty_blob; /* Error report */
14591476
Blob endScript = empty_blob; /* Script code to run at the
14601477
end. This content will be
14611478
combined into a single JS
14621479
function call, thus each
14631480
entry must end with a
14641481
semicolon. */
14651482
Stmt stmt = empty_Stmt;
1466
-#define fail(EXPR) blob_appendf EXPR; goto end_footer
14671483
14681484
login_check_credentials();
14691485
if( !g.perm.Write ){
14701486
login_needed(g.anon.Write);
14711487
return;
@@ -1472,40 +1488,31 @@
14721488
}
14731489
db_begin_transaction();
14741490
CheckinMiniInfo_init(&cimi);
14751491
14761492
style_header("File Editor");
1477
- /* As of this point, don't use return or fossil_fatal(), use
1478
- ** fail((&err,...)) instead so that we can be sure to do any
1479
- ** cleanup and end the transaction cleanly.
1493
+ /* As of this point, don't use return or fossil_fatal(). Write any
1494
+ ** error in (&err) and goto end_footer instead so that we can be
1495
+ ** sure to do any cleanup and end the transaction cleanly.
14801496
*/
14811497
if(fileedit_setup_cimi_from_p(&cimi, &err)!=0){
14821498
goto end_footer;
14831499
}
14841500
zFilename = cimi.zFilename;
14851501
zRev = cimi.zParentUuid;
1486
-
14871502
assert(zRev);
14881503
assert(zFilename);
14891504
zFileMime = mimetype_from_name(cimi.zFilename);
14901505
14911506
/********************************************************************
14921507
** All errors which "could" have happened up to this point are of a
14931508
** degree which keep us from rendering the rest of the page, and
1494
- ** thus fail() has already skipped to the end of the page to render
1495
- ** the errors. Any up-coming errors, barring malloc failure or
1496
- ** similar, are not "that" fatal. We can/should continue rendering
1497
- ** the page, then output the error message at the end.
1498
- **
1499
- ** Because we cannot intercept the output of the PREVIEW and DIFF
1500
- ** rendering, we have to delay the "real work" for those modes until
1501
- ** after the rest of the page has been rendered. In the case of
1502
- ** SAVE, we can capture all of the output, and thus can perform that
1503
- ** work before rendering, which is important so that we have the
1504
- ** proper version information when rendering the rest of the page.
1509
+ ** thus have already caused us to skipped to the end of the page to
1510
+ ** render the errors. Any up-coming errors, barring malloc failure
1511
+ ** or similar, are not "that" fatal. We can/should continue
1512
+ ** rendering the page, then output the error message at the end.
15051513
********************************************************************/
1506
-#undef fail
15071514
CX("<h1>Editing:</h1>");
15081515
CX("<p class='fileedit-hint'>");
15091516
CX("File: "
15101517
"[<a id='finfo-link' href='#'>info</a>] "
15111518
/* %R/finfo?name=%T&m=%!S */
@@ -1598,17 +1605,25 @@
15981605
? 2 : 0),
15991606
"Inherit", 0,
16001607
"Unix", 1,
16011608
"Windows", 2,
16021609
NULL);
1610
+ style_select_list_int("select-font-size",
1611
+ "editor_font_size", "Editor Font Size",
1612
+ NULL/*tooltip*/,
1613
+ 100,
1614
+ "100%", 100, "125%", 125,
1615
+ "150%", 150, "175%", 175,
1616
+ "200%", 200, NULL);
16031617
16041618
CX("</div></fieldset>") /* end of checkboxes */;
16051619
16061620
/******* Comment *******/
16071621
CX("<a id='comment'></a>");
16081622
CX("<fieldset><legend>Commit message</legend><div>");
1609
- CX("<textarea name='comment' rows='3' cols='80'>");
1623
+ CX("<textarea name='comment' rows='3' cols='80' "
1624
+ "id='fileedit-comment'>");
16101625
/* ^^^ adding the 'required' attribute means we cannot even submit
16111626
** for PREVIEW mode if it's empty :/. */
16121627
if(blob_size(&cimi.comment)){
16131628
CX("%h", blob_str(&cimi.comment));
16141629
}
@@ -1636,11 +1651,12 @@
16361651
"Wiki/Markdown", FE_RENDER_WIKI,
16371652
"HTML (iframe)", FE_RENDER_HTML,
16381653
"Plain Text", FE_RENDER_PLAIN_TEXT,
16391654
NULL);
16401655
/*
1641
- ** Set up a JS-side mapping of the FE_RENDER_xyz values
1656
+ ** Set up a JS-side mapping of the FE_RENDER_xyz values. This is
1657
+ ** used for dynamically toggling certain UI components on and off.
16421658
*/
16431659
blob_appendf(&endScript, "fossil.page.previewModes={"
16441660
"guess: %d, %d: 'guess', wiki: %d, %d: 'wiki',"
16451661
"html: %d, %d: 'html', text: %d, %d: 'text'"
16461662
"};\n",
@@ -1691,14 +1707,11 @@
16911707
db_finalize(&stmt);
16921708
}
16931709
if(blob_size(&err)){
16941710
CX("<div class='fileedit-error-report'>%s</div>",
16951711
blob_str(&err));
1696
- }else if(blob_size(&submitResult)){
1697
- CX("%b",&submitResult);
16981712
}
1699
- blob_reset(&submitResult);
17001713
blob_reset(&err);
17011714
CheckinMiniInfo_cleanup(&cimi);
17021715
style_emit_script_fetch();
17031716
fileedit_emit_page_script();
17041717
if(blob_size(&endScript)>0){
@@ -1707,9 +1720,8 @@
17071720
CX("try{\n%b\n}catch(e){console.error('Exception:',e)}\n",
17081721
&endScript);
17091722
CX("})();");
17101723
style_emit_script_tag(1);
17111724
}
1712
- db_end_transaction(0/*noting that dry-run mode will have already
1713
- ** set this to rollback mode. */);
1725
+ db_end_transaction(0);
17141726
style_footer();
17151727
}
17161728
--- src/fileedit.c
+++ src/fileedit.c
@@ -903,11 +903,11 @@
903
904 enum fileedit_render_preview_flags {
905 FE_PREVIEW_LINE_NUMBERS = 1
906 };
907 enum fileedit_render_modes {
908 /* GUESS must be 0. All others have specified values. */
909 FE_RENDER_GUESS = 0,
910 FE_RENDER_PLAIN_TEXT,
911 FE_RENDER_HTML,
912 FE_RENDER_WIKI
913 };
@@ -1015,11 +1015,11 @@
1015 db_finalize(&stmt);
1016 return zFileUuid;
1017 }
1018
1019 /*
1020 ** Helper for /filepage_xyz routes. Clears the CGI content buffer,
1021 ** sets an error status code, and queues up a JSON response in the
1022 ** form of an object:
1023 **
1024 ** {error: formatted message}
1025 **
@@ -1135,10 +1135,11 @@
1135 const char * zFilename = PD("file",P("name"));
1136 const char * zRev = P("r");
1137 int vid, frid;
1138 Blob content = empty_blob;
1139 const char * zMime;
 
1140 if(!fileedit_ajax_boostrap()
1141 || !fileedit_ajax_setup_filerev(zRev, 0, &vid,
1142 zFilename, &frid)){
1143 return;
1144 }
@@ -1149,15 +1150,24 @@
1149 }
1150
1151 /*
1152 ** WEBPAGE: fileedit_preview
1153 **
1154 ** Query parameters:
1155 **
1156 ** file=FILENAME
1157 ** render_mode=integer (FE_RENDER_xxx) (default=FE_RENDER_GUESS)
1158 ** content=text
 
 
 
 
 
 
 
 
 
 
1159 **
1160 ** User must have Write access to use this page.
1161 **
1162 ** Responds with the HTML content of the preview. On error it produces
1163 ** a JSON response as documented for fileedit_ajax_error().
@@ -1167,10 +1177,11 @@
1167 const char * zContent = P("content");
1168 int renderMode = atoi(PD("render_mode","0"));
1169 int ln = atoi(PD("ln","0"));
1170 int iframeHeight = atoi(PD("iframe_height","40"));
1171 Blob content = empty_blob;
 
1172 if(!fileedit_ajax_boostrap()
1173 || !fileedit_ajax_check_filename(zFilename)){
1174 return;
1175 }
1176 cgi_set_content_type("text/html");
@@ -1181,15 +1192,20 @@
1181 }
1182
1183 /*
1184 ** WEBPAGE: fileedit_diff
1185 **
 
 
1186 ** file=FILENAME
1187 ** sbs=integer (1=side-by-side or 0=unified)
1188 ** content=text
1189 ** r=checkin version
1190 **
 
 
 
 
1191 ** User must have Write access to use this page.
1192 **
1193 ** Responds with the HTML content of the diff. On error it produces a
1194 ** JSON response as documented for fileedit_ajax_error().
1195 */
@@ -1205,10 +1221,11 @@
1205 const char * zContent = P("content");
1206 char * zRevUuid = 0;
1207 int isSbs = atoi(PD("sbs","0"));
1208 int vid, frid;
1209 Blob content = empty_blob;
 
1210 if(!fileedit_ajax_boostrap()
1211 || !fileedit_ajax_setup_filerev(zRev, &zRevUuid, &vid,
1212 zFilename, &frid)){
1213 return;
1214 }
@@ -1221,25 +1238,27 @@
1221 fossil_free(zRevUuid);
1222 blob_reset(&content);
1223 }
1224
1225 /*
1226 ** Sets up and validates must, but not all, of p's checkin-related
1227 ** state from the CGI environment. Returns 0 on success or a suggested
1228 ** HTTP result code on error, in which case a message will have been
1229 ** written to pErr.
1230 **
1231 ** It always fails if it cannot completely resolve the 'file' and 'r'
1232 ** parameters, including verifying that the refer to a real
1233 ** file/version combination. Most others are optional.
 
 
1234 **
1235 ** Intended to be used only by /filepage and /filepage_commit.
1236 */
1237 static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr){
1238 char * zFileUuid = 0;
1239 const char * zFlag;
1240 int rc = 0, vid = 0, frid = 0;
1241
1242 #define fail(EXPR) blob_appendf EXPR; goto end_fail
1243 zFlag = PD("file",P("name"));
1244 if(zFlag==0 || !*zFlag){
1245 rc = 400;
@@ -1301,11 +1320,10 @@
1301 zFlag = P("comment_mimetype");
1302 if(zFlag){
1303 p->zCommentMimetype = mprintf("%s",zFlag);
1304 zFlag = 0;
1305 }
1306 p->zUser = mprintf("%s",g.zLogin);
1307 #define p_int(K) atoi(PD(K,"0"))
1308 if(p_int("dry_run")!=0){
1309 p->flags |= CIMINI_DRY_RUN;
1310 }
1311 if(p_int("allow_fork")!=0){
@@ -1323,23 +1341,21 @@
1323 if(p_int("prefer_delta")!=0){
1324 p->flags |= CIMINI_PREFER_DELTA;
1325 }
1326
1327 /* EOL conversion policy... */
1328 {
1329 const int eolMode = p_int("eol");
1330 switch(eolMode){
1331 case 1: p->flags |= CIMINI_CONVERT_EOL_UNIX; break;
1332 case 2: p->flags |= CIMINI_CONVERT_EOL_WINDOWS; break;
1333 default: p->flags |= CIMINI_CONVERT_EOL_INHERIT; break;
1334 }
1335 }
1336 #undef p_int
1337 /*
1338 ** TODO?: date-override date selection field. Maybe use
1339 ** an input[type=datetime-local].
1340 */
 
1341 return 0;
1342 end_fail:
1343 #undef fail
1344 fossil_free(zFileUuid);
1345 return rc ? rc : 500;
@@ -1363,25 +1379,26 @@
1363 **
1364 ** User must have Write access to use this page.
1365 **
1366 ** Responds with JSON:
1367 **
1368 ** {uuid: newUUID,
 
1369 ** manifest: text of manifest,
1370 ** dryRun: bool
1371 ** }
1372 **
1373 ** On error it produces a JSON response as documented for
1374 ** fileedit_ajax_error().
1375 */
1376 void fileedit_ajax_commit(){
1377 int newVid = 0;
1378 Blob err = empty_blob;
1379 Blob manifest = empty_blob;
1380 CheckinMiniInfo cimi;
1381 int rc;
1382 char * zNewUuid = 0;
1383
1384 if(!fileedit_ajax_boostrap()){
1385 goto end_cleanup;
1386 }
1387 db_begin_transaction();
@@ -1408,11 +1425,12 @@
1408 CX("\"uuid\":\"%j\",", zNewUuid);
1409 CX("\"dryRun\": %s,",
1410 (CIMINI_DRY_RUN & cimi.flags) ? "true" : "false");
1411 CX("\"manifest\": \"%j\"", blob_str(&manifest));
1412 CX("}");
1413 db_end_transaction(0);
 
1414 end_cleanup:
1415 fossil_free(zNewUuid);
1416 blob_reset(&err);
1417 blob_reset(&manifest);
1418 CheckinMiniInfo_cleanup(&cimi);
@@ -1453,19 +1471,17 @@
1453 CheckinMiniInfo cimi; /* Checkin state */
1454 int previewHtmlHeight = 0; /* iframe height (EMs) */
1455 int previewRenderMode = FE_RENDER_GUESS; /* preview mode */
1456 char * zFileUuid = 0; /* File content UUID */
1457 Blob err = empty_blob; /* Error report */
1458 Blob submitResult = empty_blob; /* Error report */
1459 Blob endScript = empty_blob; /* Script code to run at the
1460 end. This content will be
1461 combined into a single JS
1462 function call, thus each
1463 entry must end with a
1464 semicolon. */
1465 Stmt stmt = empty_Stmt;
1466 #define fail(EXPR) blob_appendf EXPR; goto end_footer
1467
1468 login_check_credentials();
1469 if( !g.perm.Write ){
1470 login_needed(g.anon.Write);
1471 return;
@@ -1472,40 +1488,31 @@
1472 }
1473 db_begin_transaction();
1474 CheckinMiniInfo_init(&cimi);
1475
1476 style_header("File Editor");
1477 /* As of this point, don't use return or fossil_fatal(), use
1478 ** fail((&err,...)) instead so that we can be sure to do any
1479 ** cleanup and end the transaction cleanly.
1480 */
1481 if(fileedit_setup_cimi_from_p(&cimi, &err)!=0){
1482 goto end_footer;
1483 }
1484 zFilename = cimi.zFilename;
1485 zRev = cimi.zParentUuid;
1486
1487 assert(zRev);
1488 assert(zFilename);
1489 zFileMime = mimetype_from_name(cimi.zFilename);
1490
1491 /********************************************************************
1492 ** All errors which "could" have happened up to this point are of a
1493 ** degree which keep us from rendering the rest of the page, and
1494 ** thus fail() has already skipped to the end of the page to render
1495 ** the errors. Any up-coming errors, barring malloc failure or
1496 ** similar, are not "that" fatal. We can/should continue rendering
1497 ** the page, then output the error message at the end.
1498 **
1499 ** Because we cannot intercept the output of the PREVIEW and DIFF
1500 ** rendering, we have to delay the "real work" for those modes until
1501 ** after the rest of the page has been rendered. In the case of
1502 ** SAVE, we can capture all of the output, and thus can perform that
1503 ** work before rendering, which is important so that we have the
1504 ** proper version information when rendering the rest of the page.
1505 ********************************************************************/
1506 #undef fail
1507 CX("<h1>Editing:</h1>");
1508 CX("<p class='fileedit-hint'>");
1509 CX("File: "
1510 "[<a id='finfo-link' href='#'>info</a>] "
1511 /* %R/finfo?name=%T&m=%!S */
@@ -1598,17 +1605,25 @@
1598 ? 2 : 0),
1599 "Inherit", 0,
1600 "Unix", 1,
1601 "Windows", 2,
1602 NULL);
 
 
 
 
 
 
 
1603
1604 CX("</div></fieldset>") /* end of checkboxes */;
1605
1606 /******* Comment *******/
1607 CX("<a id='comment'></a>");
1608 CX("<fieldset><legend>Commit message</legend><div>");
1609 CX("<textarea name='comment' rows='3' cols='80'>");
 
1610 /* ^^^ adding the 'required' attribute means we cannot even submit
1611 ** for PREVIEW mode if it's empty :/. */
1612 if(blob_size(&cimi.comment)){
1613 CX("%h", blob_str(&cimi.comment));
1614 }
@@ -1636,11 +1651,12 @@
1636 "Wiki/Markdown", FE_RENDER_WIKI,
1637 "HTML (iframe)", FE_RENDER_HTML,
1638 "Plain Text", FE_RENDER_PLAIN_TEXT,
1639 NULL);
1640 /*
1641 ** Set up a JS-side mapping of the FE_RENDER_xyz values
 
1642 */
1643 blob_appendf(&endScript, "fossil.page.previewModes={"
1644 "guess: %d, %d: 'guess', wiki: %d, %d: 'wiki',"
1645 "html: %d, %d: 'html', text: %d, %d: 'text'"
1646 "};\n",
@@ -1691,14 +1707,11 @@
1691 db_finalize(&stmt);
1692 }
1693 if(blob_size(&err)){
1694 CX("<div class='fileedit-error-report'>%s</div>",
1695 blob_str(&err));
1696 }else if(blob_size(&submitResult)){
1697 CX("%b",&submitResult);
1698 }
1699 blob_reset(&submitResult);
1700 blob_reset(&err);
1701 CheckinMiniInfo_cleanup(&cimi);
1702 style_emit_script_fetch();
1703 fileedit_emit_page_script();
1704 if(blob_size(&endScript)>0){
@@ -1707,9 +1720,8 @@
1707 CX("try{\n%b\n}catch(e){console.error('Exception:',e)}\n",
1708 &endScript);
1709 CX("})();");
1710 style_emit_script_tag(1);
1711 }
1712 db_end_transaction(0/*noting that dry-run mode will have already
1713 ** set this to rollback mode. */);
1714 style_footer();
1715 }
1716
--- src/fileedit.c
+++ src/fileedit.c
@@ -903,11 +903,11 @@
903
904 enum fileedit_render_preview_flags {
905 FE_PREVIEW_LINE_NUMBERS = 1
906 };
907 enum fileedit_render_modes {
908 /* GUESS must be 0. All others have unspecified values. */
909 FE_RENDER_GUESS = 0,
910 FE_RENDER_PLAIN_TEXT,
911 FE_RENDER_HTML,
912 FE_RENDER_WIKI
913 };
@@ -1015,11 +1015,11 @@
1015 db_finalize(&stmt);
1016 return zFileUuid;
1017 }
1018
1019 /*
1020 ** Helper for /fileedit_xyz routes. Clears the CGI content buffer,
1021 ** sets an error status code, and queues up a JSON response in the
1022 ** form of an object:
1023 **
1024 ** {error: formatted message}
1025 **
@@ -1135,10 +1135,11 @@
1135 const char * zFilename = PD("file",P("name"));
1136 const char * zRev = P("r");
1137 int vid, frid;
1138 Blob content = empty_blob;
1139 const char * zMime;
1140
1141 if(!fileedit_ajax_boostrap()
1142 || !fileedit_ajax_setup_filerev(zRev, 0, &vid,
1143 zFilename, &frid)){
1144 return;
1145 }
@@ -1149,15 +1150,24 @@
1150 }
1151
1152 /*
1153 ** WEBPAGE: fileedit_preview
1154 **
1155 ** Required query parameters:
1156 **
1157 ** file=FILENAME
 
1158 ** content=text
1159 **
1160 ** Optional query parameters:
1161 **
1162 ** render_mode=integer (FE_RENDER_xxx) (default=FE_RENDER_GUESS)
1163 **
1164 ** ln=0 or 1 to disable/enable line number mode in
1165 ** FE_RENDER_PLAIN_TEXT mode.
1166 **
1167 ** iframe_height=integer (default=40) Height, in EMs of HTML preview
1168 ** iframe.
1169 **
1170 ** User must have Write access to use this page.
1171 **
1172 ** Responds with the HTML content of the preview. On error it produces
1173 ** a JSON response as documented for fileedit_ajax_error().
@@ -1167,10 +1177,11 @@
1177 const char * zContent = P("content");
1178 int renderMode = atoi(PD("render_mode","0"));
1179 int ln = atoi(PD("ln","0"));
1180 int iframeHeight = atoi(PD("iframe_height","40"));
1181 Blob content = empty_blob;
1182
1183 if(!fileedit_ajax_boostrap()
1184 || !fileedit_ajax_check_filename(zFilename)){
1185 return;
1186 }
1187 cgi_set_content_type("text/html");
@@ -1181,15 +1192,20 @@
1192 }
1193
1194 /*
1195 ** WEBPAGE: fileedit_diff
1196 **
1197 ** Required query parameters:
1198 **
1199 ** file=FILENAME
 
1200 ** content=text
1201 ** r=checkin version
1202 **
1203 ** Optional parameters:
1204 **
1205 ** sbs=integer (1=side-by-side or 0=unified, default=0)
1206 **
1207 ** User must have Write access to use this page.
1208 **
1209 ** Responds with the HTML content of the diff. On error it produces a
1210 ** JSON response as documented for fileedit_ajax_error().
1211 */
@@ -1205,10 +1221,11 @@
1221 const char * zContent = P("content");
1222 char * zRevUuid = 0;
1223 int isSbs = atoi(PD("sbs","0"));
1224 int vid, frid;
1225 Blob content = empty_blob;
1226
1227 if(!fileedit_ajax_boostrap()
1228 || !fileedit_ajax_setup_filerev(zRev, &zRevUuid, &vid,
1229 zFilename, &frid)){
1230 return;
1231 }
@@ -1221,25 +1238,27 @@
1238 fossil_free(zRevUuid);
1239 blob_reset(&content);
1240 }
1241
1242 /*
1243 ** Sets up and validates most, but not all, of p's checkin-related
1244 ** state from the CGI environment. Returns 0 on success or a suggested
1245 ** HTTP result code on error, in which case a message will have been
1246 ** written to pErr.
1247 **
1248 ** It always fails if it cannot completely resolve the 'file' and 'r'
1249 ** parameters, including verifying that the refer to a real
1250 ** file/version combination and editable by the current user. All
1251 ** others are optional (at this level, anyway, but upstream code might
1252 ** require them).
1253 **
1254 ** Intended to be used only by /filepage and /filepage_commit.
1255 */
1256 static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr){
1257 char * zFileUuid = 0; /* UUID of file content */
1258 const char * zFlag; /* generic flag */
1259 int rc = 0, vid = 0, frid = 0; /* result code, checkin/file rids */
1260
1261 #define fail(EXPR) blob_appendf EXPR; goto end_fail
1262 zFlag = PD("file",P("name"));
1263 if(zFlag==0 || !*zFlag){
1264 rc = 400;
@@ -1301,11 +1320,10 @@
1320 zFlag = P("comment_mimetype");
1321 if(zFlag){
1322 p->zCommentMimetype = mprintf("%s",zFlag);
1323 zFlag = 0;
1324 }
 
1325 #define p_int(K) atoi(PD(K,"0"))
1326 if(p_int("dry_run")!=0){
1327 p->flags |= CIMINI_DRY_RUN;
1328 }
1329 if(p_int("allow_fork")!=0){
@@ -1323,23 +1341,21 @@
1341 if(p_int("prefer_delta")!=0){
1342 p->flags |= CIMINI_PREFER_DELTA;
1343 }
1344
1345 /* EOL conversion policy... */
1346 switch(p_int("eol")){
1347 case 1: p->flags |= CIMINI_CONVERT_EOL_UNIX; break;
1348 case 2: p->flags |= CIMINI_CONVERT_EOL_WINDOWS; break;
1349 default: p->flags |= CIMINI_CONVERT_EOL_INHERIT; break;
 
 
 
1350 }
1351 #undef p_int
1352 /*
1353 ** TODO?: date-override date selection field. Maybe use
1354 ** an input[type=datetime-local].
1355 */
1356 p->zUser = mprintf("%s",g.zLogin);
1357 return 0;
1358 end_fail:
1359 #undef fail
1360 fossil_free(zFileUuid);
1361 return rc ? rc : 500;
@@ -1363,25 +1379,26 @@
1379 **
1380 ** User must have Write access to use this page.
1381 **
1382 ** Responds with JSON:
1383 **
1384 ** {
1385 ** uuid: newUUID,
1386 ** manifest: text of manifest,
1387 ** dryRun: bool
1388 ** }
1389 **
1390 ** On error it produces a JSON response as documented for
1391 ** fileedit_ajax_error().
1392 */
1393 void fileedit_ajax_commit(){
1394 Blob err = empty_blob; /* Error messages */
1395 Blob manifest = empty_blob; /* raw new manifest */
1396 CheckinMiniInfo cimi; /* checkin state */
1397 int rc; /* generic result code */
1398 int newVid = 0; /* new version's RID */
1399 char * zNewUuid = 0; /* newVid's UUID */
1400
1401 if(!fileedit_ajax_boostrap()){
1402 goto end_cleanup;
1403 }
1404 db_begin_transaction();
@@ -1408,11 +1425,12 @@
1425 CX("\"uuid\":\"%j\",", zNewUuid);
1426 CX("\"dryRun\": %s,",
1427 (CIMINI_DRY_RUN & cimi.flags) ? "true" : "false");
1428 CX("\"manifest\": \"%j\"", blob_str(&manifest));
1429 CX("}");
1430 db_end_transaction(0/*noting that dry-run mode will have already
1431 ** set this to rollback mode. */);
1432 end_cleanup:
1433 fossil_free(zNewUuid);
1434 blob_reset(&err);
1435 blob_reset(&manifest);
1436 CheckinMiniInfo_cleanup(&cimi);
@@ -1453,19 +1471,17 @@
1471 CheckinMiniInfo cimi; /* Checkin state */
1472 int previewHtmlHeight = 0; /* iframe height (EMs) */
1473 int previewRenderMode = FE_RENDER_GUESS; /* preview mode */
1474 char * zFileUuid = 0; /* File content UUID */
1475 Blob err = empty_blob; /* Error report */
 
1476 Blob endScript = empty_blob; /* Script code to run at the
1477 end. This content will be
1478 combined into a single JS
1479 function call, thus each
1480 entry must end with a
1481 semicolon. */
1482 Stmt stmt = empty_Stmt;
 
1483
1484 login_check_credentials();
1485 if( !g.perm.Write ){
1486 login_needed(g.anon.Write);
1487 return;
@@ -1472,40 +1488,31 @@
1488 }
1489 db_begin_transaction();
1490 CheckinMiniInfo_init(&cimi);
1491
1492 style_header("File Editor");
1493 /* As of this point, don't use return or fossil_fatal(). Write any
1494 ** error in (&err) and goto end_footer instead so that we can be
1495 ** sure to do any cleanup and end the transaction cleanly.
1496 */
1497 if(fileedit_setup_cimi_from_p(&cimi, &err)!=0){
1498 goto end_footer;
1499 }
1500 zFilename = cimi.zFilename;
1501 zRev = cimi.zParentUuid;
 
1502 assert(zRev);
1503 assert(zFilename);
1504 zFileMime = mimetype_from_name(cimi.zFilename);
1505
1506 /********************************************************************
1507 ** All errors which "could" have happened up to this point are of a
1508 ** degree which keep us from rendering the rest of the page, and
1509 ** thus have already caused us to skipped to the end of the page to
1510 ** render the errors. Any up-coming errors, barring malloc failure
1511 ** or similar, are not "that" fatal. We can/should continue
1512 ** rendering the page, then output the error message at the end.
 
 
 
 
 
 
 
1513 ********************************************************************/
 
1514 CX("<h1>Editing:</h1>");
1515 CX("<p class='fileedit-hint'>");
1516 CX("File: "
1517 "[<a id='finfo-link' href='#'>info</a>] "
1518 /* %R/finfo?name=%T&m=%!S */
@@ -1598,17 +1605,25 @@
1605 ? 2 : 0),
1606 "Inherit", 0,
1607 "Unix", 1,
1608 "Windows", 2,
1609 NULL);
1610 style_select_list_int("select-font-size",
1611 "editor_font_size", "Editor Font Size",
1612 NULL/*tooltip*/,
1613 100,
1614 "100%", 100, "125%", 125,
1615 "150%", 150, "175%", 175,
1616 "200%", 200, NULL);
1617
1618 CX("</div></fieldset>") /* end of checkboxes */;
1619
1620 /******* Comment *******/
1621 CX("<a id='comment'></a>");
1622 CX("<fieldset><legend>Commit message</legend><div>");
1623 CX("<textarea name='comment' rows='3' cols='80' "
1624 "id='fileedit-comment'>");
1625 /* ^^^ adding the 'required' attribute means we cannot even submit
1626 ** for PREVIEW mode if it's empty :/. */
1627 if(blob_size(&cimi.comment)){
1628 CX("%h", blob_str(&cimi.comment));
1629 }
@@ -1636,11 +1651,12 @@
1651 "Wiki/Markdown", FE_RENDER_WIKI,
1652 "HTML (iframe)", FE_RENDER_HTML,
1653 "Plain Text", FE_RENDER_PLAIN_TEXT,
1654 NULL);
1655 /*
1656 ** Set up a JS-side mapping of the FE_RENDER_xyz values. This is
1657 ** used for dynamically toggling certain UI components on and off.
1658 */
1659 blob_appendf(&endScript, "fossil.page.previewModes={"
1660 "guess: %d, %d: 'guess', wiki: %d, %d: 'wiki',"
1661 "html: %d, %d: 'html', text: %d, %d: 'text'"
1662 "};\n",
@@ -1691,14 +1707,11 @@
1707 db_finalize(&stmt);
1708 }
1709 if(blob_size(&err)){
1710 CX("<div class='fileedit-error-report'>%s</div>",
1711 blob_str(&err));
 
 
1712 }
 
1713 blob_reset(&err);
1714 CheckinMiniInfo_cleanup(&cimi);
1715 style_emit_script_fetch();
1716 fileedit_emit_page_script();
1717 if(blob_size(&endScript)>0){
@@ -1707,9 +1720,8 @@
1720 CX("try{\n%b\n}catch(e){console.error('Exception:',e)}\n",
1721 &endScript);
1722 CX("})();");
1723 style_emit_script_tag(1);
1724 }
1725 db_end_transaction(0);
 
1726 style_footer();
1727 }
1728
--- src/fossil.bootstrap.js
+++ src/fossil.bootstrap.js
@@ -26,11 +26,11 @@
2626
console.debug.apply(console,args);
2727
}
2828
return this;
2929
};
3030
/*
31
-** Set message.targetElement to #fossil-status-bar, if found.
31
+** Set default message.targetElement to #fossil-status-bar, if found.
3232
*/
3333
window.fossil.message.targetElement =
3434
document.querySelector('#fossil-status-bar');
3535
/*
3636
** By default fossil.error() sends its first argument to
@@ -51,5 +51,30 @@
5151
args.unshift('Fossil error:');
5252
console.error.apply(console,args);
5353
}
5454
return this;
5555
};
56
+
57
+/**
58
+ repoUrl( repoRelativePath [,urlParams] )
59
+
60
+ Creates a URL by prepending this.rootPath to the given path
61
+ (which must be relative from the top of the site, without a
62
+ leading slash). If urlParams is a string, it must be
63
+ paramters encoded in the form "key=val&key2=val2...", WITHOUT
64
+ a leading '?'. If it's an object, all of its properties get
65
+ appended to the URL in that form.
66
+*/
67
+window.fossil.repoUrl = function(path,urlParams){
68
+ if(!urlParams) return this.rootPath+path;
69
+ const url=[this.rootPath,path];
70
+ url.push('?');
71
+ if('string'===typeof urlParams) url.push(urlParams);
72
+ else if('object'===typeof urlParams){
73
+ let k, i = 0;
74
+ for( k in urlParams ){
75
+ if(i++) url.push('&');
76
+ url.push(k,'=',encodeURIComponent(urlParams[k]));
77
+ }
78
+ }
79
+ return url.join('');
80
+};
5681
--- src/fossil.bootstrap.js
+++ src/fossil.bootstrap.js
@@ -26,11 +26,11 @@
26 console.debug.apply(console,args);
27 }
28 return this;
29 };
30 /*
31 ** Set message.targetElement to #fossil-status-bar, if found.
32 */
33 window.fossil.message.targetElement =
34 document.querySelector('#fossil-status-bar');
35 /*
36 ** By default fossil.error() sends its first argument to
@@ -51,5 +51,30 @@
51 args.unshift('Fossil error:');
52 console.error.apply(console,args);
53 }
54 return this;
55 };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
--- src/fossil.bootstrap.js
+++ src/fossil.bootstrap.js
@@ -26,11 +26,11 @@
26 console.debug.apply(console,args);
27 }
28 return this;
29 };
30 /*
31 ** Set default message.targetElement to #fossil-status-bar, if found.
32 */
33 window.fossil.message.targetElement =
34 document.querySelector('#fossil-status-bar');
35 /*
36 ** By default fossil.error() sends its first argument to
@@ -51,5 +51,30 @@
51 args.unshift('Fossil error:');
52 console.error.apply(console,args);
53 }
54 return this;
55 };
56
57 /**
58 repoUrl( repoRelativePath [,urlParams] )
59
60 Creates a URL by prepending this.rootPath to the given path
61 (which must be relative from the top of the site, without a
62 leading slash). If urlParams is a string, it must be
63 paramters encoded in the form "key=val&key2=val2...", WITHOUT
64 a leading '?'. If it's an object, all of its properties get
65 appended to the URL in that form.
66 */
67 window.fossil.repoUrl = function(path,urlParams){
68 if(!urlParams) return this.rootPath+path;
69 const url=[this.rootPath,path];
70 url.push('?');
71 if('string'===typeof urlParams) url.push(urlParams);
72 else if('object'===typeof urlParams){
73 let k, i = 0;
74 for( k in urlParams ){
75 if(i++) url.push('&');
76 url.push(k,'=',encodeURIComponent(urlParams[k]));
77 }
78 }
79 return url.join('');
80 };
81
--- src/fossil.fetch.js
+++ src/fossil.fetch.js
@@ -1,35 +1,39 @@
11
"use strict";
22
/**
33
Documented in style.c:style_emit_script_fetch(). Requires that
4
- window.fossil have already been set up.
4
+ window.fossil has already been set up (which happens via the routine
5
+ which emits this code C-side).
56
*/
67
window.fossil.fetch = function f(uri,opt){
78
if(!f.onerror){
8
- f.onerror = function(e){
9
+ f.onerror = function(e/*event or exception*/){
910
console.error("Ajax error:",e);
10
- fossil.error("Ajax error!");
11
- if(e.originalTarget && e.originalTarget.responseType==='text'){
11
+ if(e instanceof Error){
12
+ fossil.error('Exception:',e);
13
+ }
14
+ else if(e.originalTarget && e.originalTarget.responseType==='text'){
1215
const txt = e.originalTarget.responseText;
1316
try{
1417
/* The convention from the /filepage_xyz routes is to
15
- ** return error responses in JSON form if possible.
18
+ ** return error responses in JSON form if possible:
19
+ ** {error: "..."}
1620
*/
1721
const j = JSON.parse(txt);
1822
console.error("Error JSON:",j);
1923
if(j.error){ fossil.error(j.error) };
20
- }catch(e){/*ignore: not JSON*/}
24
+ }catch(e){/* Try harder */
25
+ fossil.error(txt)
26
+ }
2127
}
2228
};
29
+ f.onload = (r)=>console.debug('ajax response:',r);
2330
}
2431
if('/'===uri[0]) uri = uri.substr(1);
25
- if(!opt){
26
- opt = {};
27
- }else if('function'===typeof opt){
28
- opt={onload:opt};
29
- }
30
- if(!opt.onload) opt.onload = (r)=>console.debug('ajax response:',r);
32
+ if(!opt) opt = {};
33
+ else if('function'===typeof opt) opt={onload:opt};
34
+ if(!opt.onload) opt.onload = f.onload;
3135
if(!opt.onerror) opt.onerror = f.onerror;
3236
let payload = opt.payload, jsonResponse = false;
3337
if(payload){
3438
opt.method = 'POST';
3539
if(!(payload instanceof FormData)
@@ -41,28 +45,20 @@
4145
payload = JSON.stringify(payload);
4246
opt.contentType = 'application/json';
4347
}
4448
}
4549
}
46
- const url=[window.fossil.rootPath+uri], x=new XMLHttpRequest();
47
- if(opt.urlParams){
48
- url.push('?');
49
- if('string'===typeof opt.urlParams){
50
- url.push(opt.urlParams);
51
- }else{/*assume object*/
52
- let k, i = 0;
53
- for( k in opt.urlParams ){
54
- if(i++) url.push('&');
55
- url.push(k,'=',encodeURIComponent(opt.urlParams[k]));
56
- }
57
- }
58
- }
50
+ const url=[window.fossil.repoUrl(uri,opt.urlParams)],
51
+ x=new XMLHttpRequest();
5952
if('POST'===opt.method && 'string'===typeof opt.contentType){
6053
x.setRequestHeader('Content-Type',opt.contentType);
6154
}
6255
x.open(opt.method||'GET', url.join(''), true);
6356
if('json'===opt.responseType){
57
+ /* 'json' is an extension to the supported XHR.responseType
58
+ list. We use it as a flag to tell us to JSON.parse()
59
+ the response. */
6460
jsonResponse = true;
6561
x.responseType = 'text';
6662
}else{
6763
x.responseType = opt.responseType||'text';
6864
}
6965
--- src/fossil.fetch.js
+++ src/fossil.fetch.js
@@ -1,35 +1,39 @@
1 "use strict";
2 /**
3 Documented in style.c:style_emit_script_fetch(). Requires that
4 window.fossil have already been set up.
 
5 */
6 window.fossil.fetch = function f(uri,opt){
7 if(!f.onerror){
8 f.onerror = function(e){
9 console.error("Ajax error:",e);
10 fossil.error("Ajax error!");
11 if(e.originalTarget && e.originalTarget.responseType==='text'){
 
 
12 const txt = e.originalTarget.responseText;
13 try{
14 /* The convention from the /filepage_xyz routes is to
15 ** return error responses in JSON form if possible.
 
16 */
17 const j = JSON.parse(txt);
18 console.error("Error JSON:",j);
19 if(j.error){ fossil.error(j.error) };
20 }catch(e){/*ignore: not JSON*/}
 
 
21 }
22 };
 
23 }
24 if('/'===uri[0]) uri = uri.substr(1);
25 if(!opt){
26 opt = {};
27 }else if('function'===typeof opt){
28 opt={onload:opt};
29 }
30 if(!opt.onload) opt.onload = (r)=>console.debug('ajax response:',r);
31 if(!opt.onerror) opt.onerror = f.onerror;
32 let payload = opt.payload, jsonResponse = false;
33 if(payload){
34 opt.method = 'POST';
35 if(!(payload instanceof FormData)
@@ -41,28 +45,20 @@
41 payload = JSON.stringify(payload);
42 opt.contentType = 'application/json';
43 }
44 }
45 }
46 const url=[window.fossil.rootPath+uri], x=new XMLHttpRequest();
47 if(opt.urlParams){
48 url.push('?');
49 if('string'===typeof opt.urlParams){
50 url.push(opt.urlParams);
51 }else{/*assume object*/
52 let k, i = 0;
53 for( k in opt.urlParams ){
54 if(i++) url.push('&');
55 url.push(k,'=',encodeURIComponent(opt.urlParams[k]));
56 }
57 }
58 }
59 if('POST'===opt.method && 'string'===typeof opt.contentType){
60 x.setRequestHeader('Content-Type',opt.contentType);
61 }
62 x.open(opt.method||'GET', url.join(''), true);
63 if('json'===opt.responseType){
 
 
 
64 jsonResponse = true;
65 x.responseType = 'text';
66 }else{
67 x.responseType = opt.responseType||'text';
68 }
69
--- src/fossil.fetch.js
+++ src/fossil.fetch.js
@@ -1,35 +1,39 @@
1 "use strict";
2 /**
3 Documented in style.c:style_emit_script_fetch(). Requires that
4 window.fossil has already been set up (which happens via the routine
5 which emits this code C-side).
6 */
7 window.fossil.fetch = function f(uri,opt){
8 if(!f.onerror){
9 f.onerror = function(e/*event or exception*/){
10 console.error("Ajax error:",e);
11 if(e instanceof Error){
12 fossil.error('Exception:',e);
13 }
14 else if(e.originalTarget && e.originalTarget.responseType==='text'){
15 const txt = e.originalTarget.responseText;
16 try{
17 /* The convention from the /filepage_xyz routes is to
18 ** return error responses in JSON form if possible:
19 ** {error: "..."}
20 */
21 const j = JSON.parse(txt);
22 console.error("Error JSON:",j);
23 if(j.error){ fossil.error(j.error) };
24 }catch(e){/* Try harder */
25 fossil.error(txt)
26 }
27 }
28 };
29 f.onload = (r)=>console.debug('ajax response:',r);
30 }
31 if('/'===uri[0]) uri = uri.substr(1);
32 if(!opt) opt = {};
33 else if('function'===typeof opt) opt={onload:opt};
34 if(!opt.onload) opt.onload = f.onload;
 
 
 
35 if(!opt.onerror) opt.onerror = f.onerror;
36 let payload = opt.payload, jsonResponse = false;
37 if(payload){
38 opt.method = 'POST';
39 if(!(payload instanceof FormData)
@@ -41,28 +45,20 @@
45 payload = JSON.stringify(payload);
46 opt.contentType = 'application/json';
47 }
48 }
49 }
50 const url=[window.fossil.repoUrl(uri,opt.urlParams)],
51 x=new XMLHttpRequest();
 
 
 
 
 
 
 
 
 
 
 
52 if('POST'===opt.method && 'string'===typeof opt.contentType){
53 x.setRequestHeader('Content-Type',opt.contentType);
54 }
55 x.open(opt.method||'GET', url.join(''), true);
56 if('json'===opt.responseType){
57 /* 'json' is an extension to the supported XHR.responseType
58 list. We use it as a flag to tell us to JSON.parse()
59 the response. */
60 jsonResponse = true;
61 x.responseType = 'text';
62 }else{
63 x.responseType = opt.responseType||'text';
64 }
65
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -6,11 +6,12 @@
66
*/
77
const E = (s)=>document.querySelector(s);
88
window.addEventListener("load", function() {
99
const P = fossil.page;
1010
P.e = {
11
- editor: E('#fileedit-content'),
11
+ taEditor: E('#fileedit-content'),
12
+ taComment: E('#fileedit-comment'),
1213
ajaxContentTarget: E('#ajax-target'),
1314
form: E('#fileedit-form'),
1415
btnPreview: E("#fileedit-btn-preview"),
1516
btnDiffSbs: E("#fileedit-btn-diffsbs"),
1617
btnDiffU: E("#fileedit-btn-diffu"),
@@ -48,11 +49,10 @@
4849
certain preview options depending on the current
4950
preview mode...
5051
*/
5152
const selectPreviewMode =
5253
P.e.selectPreviewModeWrap.querySelector('select');
53
- console.debug('selectPreviewMode =',selectPreviewMode);
5454
selectPreviewMode.addEventListener(
5555
"change",function(e){
5656
const mode = e.target.value,
5757
name = P.previewModes[mode],
5858
hide = [], unhide = [];
@@ -70,10 +70,22 @@
7070
}, false
7171
);
7272
selectPreviewMode.dispatchEvent(
7373
// Force UI update
7474
new Event('change',{target:selectPreviewMode})
75
+ );
76
+ const selectFontSize = E('select[name=editor_font_size]');
77
+ selectFontSize.addEventListener(
78
+ "change",function(e){
79
+ P.e.taEditor.className = e.target.className.replace(
80
+ /\bfont-size-\d+/g, '' );
81
+ P.e.taEditor.classList.add('font-size-'+e.target.value);
82
+ }, false
83
+ );
84
+ selectFontSize.dispatchEvent(
85
+ // Force UI update
86
+ new Event('change',{target:selectFontSize})
7587
);
7688
}, false);
7789
7890
7991
@@ -134,11 +146,11 @@
134146
fossil.page.preview = function(){
135147
if(!this.finfo){
136148
fossil.error("No content is loaded.");
137149
return this;
138150
}
139
- const content = this.e.editor.value,
151
+ const content = this.e.taEditor.value,
140152
target = this.e.ajaxContentTarget;
141153
const updateView = function(c){
142154
target.innerHTML = [
143155
"<div class='fileedit-preview'>",
144156
"<div>Preview</div>",
@@ -176,11 +188,11 @@
176188
if(!this.finfo){
177189
fossil.error("No content is loaded.");
178190
return this;
179191
}
180192
const self = this;
181
- const content = this.e.editor.value,
193
+ const content = this.e.taEditor.value,
182194
target = this.e.ajaxContentTarget;
183195
const updateView = function(c){
184196
target.innerHTML = [
185197
"<div class='fileedit-diff'>",
186198
"<div>Diff <code>[",
@@ -219,11 +231,11 @@
219231
if(!this.finfo){
220232
fossil.error("No content is loaded.");
221233
return this;
222234
}
223235
const self = this;
224
- const content = this.e.editor.value,
236
+ const content = this.e.taEditor.value,
225237
target = this.e.ajaxContentTarget,
226238
cbDryRun = E('[name=dry_run]'),
227239
isDryRun = cbDryRun.checked,
228240
filename = this.finfo.file;
229241
if(!f.updateView){
@@ -234,18 +246,22 @@
234246
": ", c.uuid.substring(0,16),"</h3>",
235247
"<code class='fileedit-manifest'>",
236248
c.manifest,
237249
"</code></pre>"
238250
].join('');
239
- fossil.message(
240
- c.dryRun ? 'Committed (dry run):' : 'Committed:',
241
- c.uuid
242
- );
251
+ const msg = [
252
+ 'Committed',
253
+ c.dryRun ? '(dry run)' : '',
254
+ '[', c.uuid,'].'
255
+ ];
243256
if(!c.dryRun){
257
+ msg.push('Re-activating dry-run mode.');
258
+ self.e.taComment.value = '';
244259
cbDryRun.checked = true;
245260
fossil.page.updateVersion(filename, c.uuid);
246261
}
262
+ fossil.message.apply(fossil, msg);
247263
};
248264
}
249265
if(!content){
250266
f.updateView('');
251267
return this;
252268
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -6,11 +6,12 @@
6 */
7 const E = (s)=>document.querySelector(s);
8 window.addEventListener("load", function() {
9 const P = fossil.page;
10 P.e = {
11 editor: E('#fileedit-content'),
 
12 ajaxContentTarget: E('#ajax-target'),
13 form: E('#fileedit-form'),
14 btnPreview: E("#fileedit-btn-preview"),
15 btnDiffSbs: E("#fileedit-btn-diffsbs"),
16 btnDiffU: E("#fileedit-btn-diffu"),
@@ -48,11 +49,10 @@
48 certain preview options depending on the current
49 preview mode...
50 */
51 const selectPreviewMode =
52 P.e.selectPreviewModeWrap.querySelector('select');
53 console.debug('selectPreviewMode =',selectPreviewMode);
54 selectPreviewMode.addEventListener(
55 "change",function(e){
56 const mode = e.target.value,
57 name = P.previewModes[mode],
58 hide = [], unhide = [];
@@ -70,10 +70,22 @@
70 }, false
71 );
72 selectPreviewMode.dispatchEvent(
73 // Force UI update
74 new Event('change',{target:selectPreviewMode})
 
 
 
 
 
 
 
 
 
 
 
 
75 );
76 }, false);
77
78
79
@@ -134,11 +146,11 @@
134 fossil.page.preview = function(){
135 if(!this.finfo){
136 fossil.error("No content is loaded.");
137 return this;
138 }
139 const content = this.e.editor.value,
140 target = this.e.ajaxContentTarget;
141 const updateView = function(c){
142 target.innerHTML = [
143 "<div class='fileedit-preview'>",
144 "<div>Preview</div>",
@@ -176,11 +188,11 @@
176 if(!this.finfo){
177 fossil.error("No content is loaded.");
178 return this;
179 }
180 const self = this;
181 const content = this.e.editor.value,
182 target = this.e.ajaxContentTarget;
183 const updateView = function(c){
184 target.innerHTML = [
185 "<div class='fileedit-diff'>",
186 "<div>Diff <code>[",
@@ -219,11 +231,11 @@
219 if(!this.finfo){
220 fossil.error("No content is loaded.");
221 return this;
222 }
223 const self = this;
224 const content = this.e.editor.value,
225 target = this.e.ajaxContentTarget,
226 cbDryRun = E('[name=dry_run]'),
227 isDryRun = cbDryRun.checked,
228 filename = this.finfo.file;
229 if(!f.updateView){
@@ -234,18 +246,22 @@
234 ": ", c.uuid.substring(0,16),"</h3>",
235 "<code class='fileedit-manifest'>",
236 c.manifest,
237 "</code></pre>"
238 ].join('');
239 fossil.message(
240 c.dryRun ? 'Committed (dry run):' : 'Committed:',
241 c.uuid
242 );
 
243 if(!c.dryRun){
 
 
244 cbDryRun.checked = true;
245 fossil.page.updateVersion(filename, c.uuid);
246 }
 
247 };
248 }
249 if(!content){
250 f.updateView('');
251 return this;
252
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -6,11 +6,12 @@
6 */
7 const E = (s)=>document.querySelector(s);
8 window.addEventListener("load", function() {
9 const P = fossil.page;
10 P.e = {
11 taEditor: E('#fileedit-content'),
12 taComment: E('#fileedit-comment'),
13 ajaxContentTarget: E('#ajax-target'),
14 form: E('#fileedit-form'),
15 btnPreview: E("#fileedit-btn-preview"),
16 btnDiffSbs: E("#fileedit-btn-diffsbs"),
17 btnDiffU: E("#fileedit-btn-diffu"),
@@ -48,11 +49,10 @@
49 certain preview options depending on the current
50 preview mode...
51 */
52 const selectPreviewMode =
53 P.e.selectPreviewModeWrap.querySelector('select');
 
54 selectPreviewMode.addEventListener(
55 "change",function(e){
56 const mode = e.target.value,
57 name = P.previewModes[mode],
58 hide = [], unhide = [];
@@ -70,10 +70,22 @@
70 }, false
71 );
72 selectPreviewMode.dispatchEvent(
73 // Force UI update
74 new Event('change',{target:selectPreviewMode})
75 );
76 const selectFontSize = E('select[name=editor_font_size]');
77 selectFontSize.addEventListener(
78 "change",function(e){
79 P.e.taEditor.className = e.target.className.replace(
80 /\bfont-size-\d+/g, '' );
81 P.e.taEditor.classList.add('font-size-'+e.target.value);
82 }, false
83 );
84 selectFontSize.dispatchEvent(
85 // Force UI update
86 new Event('change',{target:selectFontSize})
87 );
88 }, false);
89
90
91
@@ -134,11 +146,11 @@
146 fossil.page.preview = function(){
147 if(!this.finfo){
148 fossil.error("No content is loaded.");
149 return this;
150 }
151 const content = this.e.taEditor.value,
152 target = this.e.ajaxContentTarget;
153 const updateView = function(c){
154 target.innerHTML = [
155 "<div class='fileedit-preview'>",
156 "<div>Preview</div>",
@@ -176,11 +188,11 @@
188 if(!this.finfo){
189 fossil.error("No content is loaded.");
190 return this;
191 }
192 const self = this;
193 const content = this.e.taEditor.value,
194 target = this.e.ajaxContentTarget;
195 const updateView = function(c){
196 target.innerHTML = [
197 "<div class='fileedit-diff'>",
198 "<div>Diff <code>[",
@@ -219,11 +231,11 @@
231 if(!this.finfo){
232 fossil.error("No content is loaded.");
233 return this;
234 }
235 const self = this;
236 const content = this.e.taEditor.value,
237 target = this.e.ajaxContentTarget,
238 cbDryRun = E('[name=dry_run]'),
239 isDryRun = cbDryRun.checked,
240 filename = this.finfo.file;
241 if(!f.updateView){
@@ -234,18 +246,22 @@
246 ": ", c.uuid.substring(0,16),"</h3>",
247 "<code class='fileedit-manifest'>",
248 c.manifest,
249 "</code></pre>"
250 ].join('');
251 const msg = [
252 'Committed',
253 c.dryRun ? '(dry run)' : '',
254 '[', c.uuid,'].'
255 ];
256 if(!c.dryRun){
257 msg.push('Re-activating dry-run mode.');
258 self.e.taComment.value = '';
259 cbDryRun.checked = true;
260 fossil.page.updateVersion(filename, c.uuid);
261 }
262 fossil.message.apply(fossil, msg);
263 };
264 }
265 if(!content){
266 f.updateView('');
267 return this;
268
+14 -6
--- src/style.c
+++ src/style.c
@@ -1424,11 +1424,11 @@
14241424
va_end(vargs);
14251425
}
14261426
14271427
14281428
/*
1429
-** If passed 0, it emits a script opener tag with this session's
1429
+** If passed 0, it emits a script opener tag with this request's
14301430
** nonce. If passed non-0 it emits a script closing tag. The very
14311431
** first time it is called, it emits some bootstrapping JS code
14321432
** immediately after the script opener. Specifically, it defines
14331433
** window.fossil if it's not already defined, and may set some
14341434
** properties on it.
@@ -1470,13 +1470,13 @@
14701470
** fetch(). It calls style_emit_script_tag(), which may inject
14711471
** other JS bootstrapping bits.
14721472
**
14731473
** JS usages:
14741474
**
1475
-** fossilFetch( URI, onLoadCallback );
1475
+** fossil.fetch( URI [, onLoadCallback] );
14761476
**
1477
-** fossilFetch( URI, optionsObject );
1477
+** fossil.fetch( URI [, optionsObject = {}] );
14781478
**
14791479
** Noting that URI must be relative to the top of the repository and
14801480
** should not start with a slash (if it does, it is stripped). It gets
14811481
** the equivalent of "%R/" prepended to it.
14821482
**
@@ -1485,11 +1485,11 @@
14851485
**
14861486
** - onload: callback(responseData) (default = output response to
14871487
** the console).
14881488
**
14891489
** - onerror: callback(XHR onload event | exception)
1490
-** (default = output event or exception to the console).
1490
+** (default = event or exception to the console).
14911491
**
14921492
** - method: 'POST' | 'GET' (default = 'GET')
14931493
**
14941494
** - payload: anything acceptable by XHR2.send(ARG) (DOMString,
14951495
** Document, FormData, Blob, File, ArrayBuffer), or a plain object
@@ -1513,12 +1513,20 @@
15131513
** URI-encoded list of params in the form "key1=val1&key2=val2...",
15141514
** with NO leading '?'. If it is an object, all of its properties
15151515
** get converted to that form. Either way, the parameters get
15161516
** appended to the URL.
15171517
**
1518
-** Returns this object, noting that the XHR request is still in
1519
-** transit (or has yet to be sent) when that happens.
1518
+** When an options object does not provide onload() or onerror()
1519
+** handlers of its own, this function falls back to
1520
+** fossil.fetch.onload() and fossil.fetch.onerror() as defaults. The
1521
+** default implementations route the data through the dev console and
1522
+** (for onerror()) through fossil.error(). Individual pages may
1523
+** overwrite those members to provide default implementations suitable
1524
+** for the page's use.
1525
+**
1526
+** Returns this object, noting that the XHR request is asynchronous,
1527
+** and still in transit (or has yet to be sent) when that happens.
15201528
*/
15211529
void style_emit_script_fetch(){
15221530
static int once = 0;
15231531
if(0==once){
15241532
once = 1;
15251533
--- src/style.c
+++ src/style.c
@@ -1424,11 +1424,11 @@
1424 va_end(vargs);
1425 }
1426
1427
1428 /*
1429 ** If passed 0, it emits a script opener tag with this session's
1430 ** nonce. If passed non-0 it emits a script closing tag. The very
1431 ** first time it is called, it emits some bootstrapping JS code
1432 ** immediately after the script opener. Specifically, it defines
1433 ** window.fossil if it's not already defined, and may set some
1434 ** properties on it.
@@ -1470,13 +1470,13 @@
1470 ** fetch(). It calls style_emit_script_tag(), which may inject
1471 ** other JS bootstrapping bits.
1472 **
1473 ** JS usages:
1474 **
1475 ** fossilFetch( URI, onLoadCallback );
1476 **
1477 ** fossilFetch( URI, optionsObject );
1478 **
1479 ** Noting that URI must be relative to the top of the repository and
1480 ** should not start with a slash (if it does, it is stripped). It gets
1481 ** the equivalent of "%R/" prepended to it.
1482 **
@@ -1485,11 +1485,11 @@
1485 **
1486 ** - onload: callback(responseData) (default = output response to
1487 ** the console).
1488 **
1489 ** - onerror: callback(XHR onload event | exception)
1490 ** (default = output event or exception to the console).
1491 **
1492 ** - method: 'POST' | 'GET' (default = 'GET')
1493 **
1494 ** - payload: anything acceptable by XHR2.send(ARG) (DOMString,
1495 ** Document, FormData, Blob, File, ArrayBuffer), or a plain object
@@ -1513,12 +1513,20 @@
1513 ** URI-encoded list of params in the form "key1=val1&key2=val2...",
1514 ** with NO leading '?'. If it is an object, all of its properties
1515 ** get converted to that form. Either way, the parameters get
1516 ** appended to the URL.
1517 **
1518 ** Returns this object, noting that the XHR request is still in
1519 ** transit (or has yet to be sent) when that happens.
 
 
 
 
 
 
 
 
1520 */
1521 void style_emit_script_fetch(){
1522 static int once = 0;
1523 if(0==once){
1524 once = 1;
1525
--- src/style.c
+++ src/style.c
@@ -1424,11 +1424,11 @@
1424 va_end(vargs);
1425 }
1426
1427
1428 /*
1429 ** If passed 0, it emits a script opener tag with this request's
1430 ** nonce. If passed non-0 it emits a script closing tag. The very
1431 ** first time it is called, it emits some bootstrapping JS code
1432 ** immediately after the script opener. Specifically, it defines
1433 ** window.fossil if it's not already defined, and may set some
1434 ** properties on it.
@@ -1470,13 +1470,13 @@
1470 ** fetch(). It calls style_emit_script_tag(), which may inject
1471 ** other JS bootstrapping bits.
1472 **
1473 ** JS usages:
1474 **
1475 ** fossil.fetch( URI [, onLoadCallback] );
1476 **
1477 ** fossil.fetch( URI [, optionsObject = {}] );
1478 **
1479 ** Noting that URI must be relative to the top of the repository and
1480 ** should not start with a slash (if it does, it is stripped). It gets
1481 ** the equivalent of "%R/" prepended to it.
1482 **
@@ -1485,11 +1485,11 @@
1485 **
1486 ** - onload: callback(responseData) (default = output response to
1487 ** the console).
1488 **
1489 ** - onerror: callback(XHR onload event | exception)
1490 ** (default = event or exception to the console).
1491 **
1492 ** - method: 'POST' | 'GET' (default = 'GET')
1493 **
1494 ** - payload: anything acceptable by XHR2.send(ARG) (DOMString,
1495 ** Document, FormData, Blob, File, ArrayBuffer), or a plain object
@@ -1513,12 +1513,20 @@
1513 ** URI-encoded list of params in the form "key1=val1&key2=val2...",
1514 ** with NO leading '?'. If it is an object, all of its properties
1515 ** get converted to that form. Either way, the parameters get
1516 ** appended to the URL.
1517 **
1518 ** When an options object does not provide onload() or onerror()
1519 ** handlers of its own, this function falls back to
1520 ** fossil.fetch.onload() and fossil.fetch.onerror() as defaults. The
1521 ** default implementations route the data through the dev console and
1522 ** (for onerror()) through fossil.error(). Individual pages may
1523 ** overwrite those members to provide default implementations suitable
1524 ** for the page's use.
1525 **
1526 ** Returns this object, noting that the XHR request is asynchronous,
1527 ** and still in transit (or has yet to be sent) when that happens.
1528 */
1529 void style_emit_script_fetch(){
1530 static int once = 0;
1531 if(0==once){
1532 once = 1;
1533

Keyboard Shortcuts

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