Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Numerous minor cleanups. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | fileedit-ajaxify |
| Files: | files | file ages | folders |
| SHA3-256: |
f54ac21745eb59e628cfd19bccc5f3c3 |
| User & Date: | stephan 2020-05-05 08:41:45.611 |
Context
|
2020-05-05
| ||
| 11:51 | /fileedit_content now only uses application/octet-stream for files which explicitly have that type via mimetype_by_name() or which look like binary content, falling back to text/plain, per suggestion in the discussion thread. ... (check-in: 4270ecb3a2 user: stephan tags: fileedit-ajaxify) | |
| 08:41 | Numerous minor cleanups. ... (check-in: f54ac21745 user: stephan tags: fileedit-ajaxify) | |
| 06:48 | Ajaxified commit. All that's left is cleanup and prettification. ... (check-in: 1a6c5090ce user: stephan tags: fileedit-ajaxify) | |
Changes
Changes to src/default_css.txt.
| ︙ | ︙ | |||
967 968 969 970 971 972 973 |
.input-with-label > span {
margin: 0 0.25em 0 0.25em;
vertical-align: middle;
}
.hidden {
display: none;
}
| > > > > > > > > > > > > > > > | 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 |
.input-with-label > span {
margin: 0 0.25em 0 0.25em;
vertical-align: middle;
}
.hidden {
display: none;
}
.font-size-100 {
font-size: 100%;
}
.font-size-125 {
font-size: 125%;
}
.font-size-150 {
font-size: 150%;
}
.font-size-175 {
font-size: 175%;
}
.font-size-200 {
font-size: 200%;
}
|
Changes to src/fileedit.c.
| ︙ | ︙ | |||
901 902 903 904 905 906 907 |
}
enum fileedit_render_preview_flags {
FE_PREVIEW_LINE_NUMBERS = 1
};
enum fileedit_render_modes {
| | | 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 |
}
enum fileedit_render_preview_flags {
FE_PREVIEW_LINE_NUMBERS = 1
};
enum fileedit_render_modes {
/* GUESS must be 0. All others have unspecified values. */
FE_RENDER_GUESS = 0,
FE_RENDER_PLAIN_TEXT,
FE_RENDER_HTML,
FE_RENDER_WIKI
};
static int fileedit_render_mode_for_mimetype(const char * zMimetype){
|
| ︙ | ︙ | |||
1013 1014 1015 1016 1017 1018 1019 |
}
}
db_finalize(&stmt);
return zFileUuid;
}
/*
| | | 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 |
}
}
db_finalize(&stmt);
return zFileUuid;
}
/*
** Helper for /fileedit_xyz routes. Clears the CGI content buffer,
** sets an error status code, and queues up a JSON response in the
** form of an object:
**
** {error: formatted message}
**
** After calling this, the caller should immediately return.
*/
|
| ︙ | ︙ | |||
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 |
*/
void fileedit_ajax_content(){
const char * zFilename = PD("file",P("name"));
const char * zRev = P("r");
int vid, frid;
Blob content = empty_blob;
const char * zMime;
if(!fileedit_ajax_boostrap()
|| !fileedit_ajax_setup_filerev(zRev, 0, &vid,
zFilename, &frid)){
return;
}
zMime = mimetype_from_name(zFilename);
content_get(frid, &content);
cgi_set_content_type(zMime ? zMime : "application/octet-stream");
cgi_set_content(&content);
}
/*
** WEBPAGE: fileedit_preview
**
| > | > > > > | > > > > > > > > < > > > > > | | > > | | | | 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 |
*/
void fileedit_ajax_content(){
const char * zFilename = PD("file",P("name"));
const char * zRev = P("r");
int vid, frid;
Blob content = empty_blob;
const char * zMime;
if(!fileedit_ajax_boostrap()
|| !fileedit_ajax_setup_filerev(zRev, 0, &vid,
zFilename, &frid)){
return;
}
zMime = mimetype_from_name(zFilename);
content_get(frid, &content);
cgi_set_content_type(zMime ? zMime : "application/octet-stream");
cgi_set_content(&content);
}
/*
** WEBPAGE: fileedit_preview
**
** Required query parameters:
**
** file=FILENAME
** content=text
**
** Optional query parameters:
**
** render_mode=integer (FE_RENDER_xxx) (default=FE_RENDER_GUESS)
**
** ln=0 or 1 to disable/enable line number mode in
** FE_RENDER_PLAIN_TEXT mode.
**
** iframe_height=integer (default=40) Height, in EMs of HTML preview
** iframe.
**
** User must have Write access to use this page.
**
** Responds with the HTML content of the preview. On error it produces
** a JSON response as documented for fileedit_ajax_error().
*/
void fileedit_ajax_preview(){
const char * zFilename = PD("file",P("name"));
const char * zContent = P("content");
int renderMode = atoi(PD("render_mode","0"));
int ln = atoi(PD("ln","0"));
int iframeHeight = atoi(PD("iframe_height","40"));
Blob content = empty_blob;
if(!fileedit_ajax_boostrap()
|| !fileedit_ajax_check_filename(zFilename)){
return;
}
cgi_set_content_type("text/html");
blob_init(&content, zContent, -1);
fileedit_render_preview(&content, zFilename,
ln ? FE_PREVIEW_LINE_NUMBERS : 0,
renderMode, iframeHeight);
}
/*
** WEBPAGE: fileedit_diff
**
** Required query parameters:
**
** file=FILENAME
** content=text
** r=checkin version
**
** Optional parameters:
**
** sbs=integer (1=side-by-side or 0=unified, default=0)
**
** User must have Write access to use this page.
**
** Responds with the HTML content of the diff. On error it produces a
** JSON response as documented for fileedit_ajax_error().
*/
void fileedit_ajax_diff(){
/*
** Reminder: we only need the filename to perform valdiation
** against fileedit_is_editable(), else this route could be
** abused to get diffs against content disallowed by the
** whitelist.
*/
const char * zFilename = PD("file",P("name"));
const char * zRev = P("r");
const char * zContent = P("content");
char * zRevUuid = 0;
int isSbs = atoi(PD("sbs","0"));
int vid, frid;
Blob content = empty_blob;
if(!fileedit_ajax_boostrap()
|| !fileedit_ajax_setup_filerev(zRev, &zRevUuid, &vid,
zFilename, &frid)){
return;
}
if(!zContent){
zContent = "";
}
cgi_set_content_type("text/html");
blob_init(&content, zContent, -1);
fileedit_render_diff(&content, frid, zRevUuid, isSbs);
fossil_free(zRevUuid);
blob_reset(&content);
}
/*
** Sets up and validates most, but not all, of p's checkin-related
** state from the CGI environment. Returns 0 on success or a suggested
** HTTP result code on error, in which case a message will have been
** written to pErr.
**
** It always fails if it cannot completely resolve the 'file' and 'r'
** parameters, including verifying that the refer to a real
** file/version combination and editable by the current user. All
** others are optional (at this level, anyway, but upstream code might
** require them).
**
** Intended to be used only by /filepage and /filepage_commit.
*/
static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr){
char * zFileUuid = 0; /* UUID of file content */
const char * zFlag; /* generic flag */
int rc = 0, vid = 0, frid = 0; /* result code, checkin/file rids */
#define fail(EXPR) blob_appendf EXPR; goto end_fail
zFlag = PD("file",P("name"));
if(zFlag==0 || !*zFlag){
rc = 400;
fail((pErr,"Missing required 'file' parameter."));
}
|
| ︙ | ︙ | |||
1299 1300 1301 1302 1303 1304 1305 |
blob_append(&p->comment, zFlag, -1);
}
zFlag = P("comment_mimetype");
if(zFlag){
p->zCommentMimetype = mprintf("%s",zFlag);
zFlag = 0;
}
| < | 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 |
blob_append(&p->comment, zFlag, -1);
}
zFlag = P("comment_mimetype");
if(zFlag){
p->zCommentMimetype = mprintf("%s",zFlag);
zFlag = 0;
}
#define p_int(K) atoi(PD(K,"0"))
if(p_int("dry_run")!=0){
p->flags |= CIMINI_DRY_RUN;
}
if(p_int("allow_fork")!=0){
p->flags |= CIMINI_ALLOW_FORK;
}
|
| ︙ | ︙ | |||
1321 1322 1323 1324 1325 1326 1327 |
p->flags |= CIMINI_ALLOW_MERGE_MARKER;
}
if(p_int("prefer_delta")!=0){
p->flags |= CIMINI_PREFER_DELTA;
}
/* EOL conversion policy... */
| < | < | | | < > | 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 |
p->flags |= CIMINI_ALLOW_MERGE_MARKER;
}
if(p_int("prefer_delta")!=0){
p->flags |= CIMINI_PREFER_DELTA;
}
/* EOL conversion policy... */
switch(p_int("eol")){
case 1: p->flags |= CIMINI_CONVERT_EOL_UNIX; break;
case 2: p->flags |= CIMINI_CONVERT_EOL_WINDOWS; break;
default: p->flags |= CIMINI_CONVERT_EOL_INHERIT; break;
}
#undef p_int
/*
** TODO?: date-override date selection field. Maybe use
** an input[type=datetime-local].
*/
p->zUser = mprintf("%s",g.zLogin);
return 0;
end_fail:
#undef fail
fossil_free(zFileUuid);
return rc ? rc : 500;
}
|
| ︙ | ︙ | |||
1361 1362 1363 1364 1365 1366 1367 | ** dry_run=int (1 or 0) ** ** ** User must have Write access to use this page. ** ** Responds with JSON: ** | > | < | | | | > | | 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 |
** dry_run=int (1 or 0)
**
**
** User must have Write access to use this page.
**
** Responds with JSON:
**
** {
** uuid: newUUID,
** manifest: text of manifest,
** dryRun: bool
** }
**
** On error it produces a JSON response as documented for
** fileedit_ajax_error().
*/
void fileedit_ajax_commit(){
Blob err = empty_blob; /* Error messages */
Blob manifest = empty_blob; /* raw new manifest */
CheckinMiniInfo cimi; /* checkin state */
int rc; /* generic result code */
int newVid = 0; /* new version's RID */
char * zNewUuid = 0; /* newVid's UUID */
if(!fileedit_ajax_boostrap()){
goto end_cleanup;
}
db_begin_transaction();
CheckinMiniInfo_init(&cimi);
rc = fileedit_setup_cimi_from_p(&cimi,&err);
|
| ︙ | ︙ | |||
1406 1407 1408 1409 1410 1411 1412 |
cgi_set_content_type("application/json");
CX("{");
CX("\"uuid\":\"%j\",", zNewUuid);
CX("\"dryRun\": %s,",
(CIMINI_DRY_RUN & cimi.flags) ? "true" : "false");
CX("\"manifest\": \"%j\"", blob_str(&manifest));
CX("}");
| | > | 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 |
cgi_set_content_type("application/json");
CX("{");
CX("\"uuid\":\"%j\",", zNewUuid);
CX("\"dryRun\": %s,",
(CIMINI_DRY_RUN & cimi.flags) ? "true" : "false");
CX("\"manifest\": \"%j\"", blob_str(&manifest));
CX("}");
db_end_transaction(0/*noting that dry-run mode will have already
** set this to rollback mode. */);
end_cleanup:
fossil_free(zNewUuid);
blob_reset(&err);
blob_reset(&manifest);
CheckinMiniInfo_cleanup(&cimi);
}
|
| ︙ | ︙ | |||
1451 1452 1453 1454 1455 1456 1457 | const char * zRev; /* checkin version */ const char * zFileMime = 0; /* File mime type guess */ CheckinMiniInfo cimi; /* Checkin state */ int previewHtmlHeight = 0; /* iframe height (EMs) */ int previewRenderMode = FE_RENDER_GUESS; /* preview mode */ char * zFileUuid = 0; /* File content UUID */ Blob err = empty_blob; /* Error report */ | < < | | | < | | | | < < < < < < < < | 1469 1470 1471 1472 1473 1474 1475 1476 1477 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 1517 1518 1519 1520 |
const char * zRev; /* checkin version */
const char * zFileMime = 0; /* File mime type guess */
CheckinMiniInfo cimi; /* Checkin state */
int previewHtmlHeight = 0; /* iframe height (EMs) */
int previewRenderMode = FE_RENDER_GUESS; /* preview mode */
char * zFileUuid = 0; /* File content UUID */
Blob err = empty_blob; /* Error report */
Blob endScript = empty_blob; /* Script code to run at the
end. This content will be
combined into a single JS
function call, thus each
entry must end with a
semicolon. */
Stmt stmt = empty_Stmt;
login_check_credentials();
if( !g.perm.Write ){
login_needed(g.anon.Write);
return;
}
db_begin_transaction();
CheckinMiniInfo_init(&cimi);
style_header("File Editor");
/* As of this point, don't use return or fossil_fatal(). Write any
** error in (&err) and goto end_footer instead so that we can be
** sure to do any cleanup and end the transaction cleanly.
*/
if(fileedit_setup_cimi_from_p(&cimi, &err)!=0){
goto end_footer;
}
zFilename = cimi.zFilename;
zRev = cimi.zParentUuid;
assert(zRev);
assert(zFilename);
zFileMime = mimetype_from_name(cimi.zFilename);
/********************************************************************
** All errors which "could" have happened up to this point are of a
** degree which keep us from rendering the rest of the page, and
** thus have already caused us to skipped to the end of the page to
** render the errors. Any up-coming errors, barring malloc failure
** or similar, are not "that" fatal. We can/should continue
** rendering the page, then output the error message at the end.
********************************************************************/
CX("<h1>Editing:</h1>");
CX("<p class='fileedit-hint'>");
CX("File: "
"[<a id='finfo-link' href='#'>info</a>] "
/* %R/finfo?name=%T&m=%!S */
"<code id='finfo-file-name'>(loading)</code><br>");
CX("Checkin Version: "
|
| ︙ | ︙ | |||
1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 |
(cimi.flags & CIMINI_CONVERT_EOL_UNIX)
? 1 : (cimi.flags & CIMINI_CONVERT_EOL_WINDOWS
? 2 : 0),
"Inherit", 0,
"Unix", 1,
"Windows", 2,
NULL);
CX("</div></fieldset>") /* end of checkboxes */;
/******* Comment *******/
CX("<a id='comment'></a>");
CX("<fieldset><legend>Commit message</legend><div>");
| > > > > > > > | > | 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 |
(cimi.flags & CIMINI_CONVERT_EOL_UNIX)
? 1 : (cimi.flags & CIMINI_CONVERT_EOL_WINDOWS
? 2 : 0),
"Inherit", 0,
"Unix", 1,
"Windows", 2,
NULL);
style_select_list_int("select-font-size",
"editor_font_size", "Editor Font Size",
NULL/*tooltip*/,
100,
"100%", 100, "125%", 125,
"150%", 150, "175%", 175,
"200%", 200, NULL);
CX("</div></fieldset>") /* end of checkboxes */;
/******* Comment *******/
CX("<a id='comment'></a>");
CX("<fieldset><legend>Commit message</legend><div>");
CX("<textarea name='comment' rows='3' cols='80' "
"id='fileedit-comment'>");
/* ^^^ adding the 'required' attribute means we cannot even submit
** for PREVIEW mode if it's empty :/. */
if(blob_size(&cimi.comment)){
CX("%h", blob_str(&cimi.comment));
}
CX("</textarea>\n");
CX("<div class='fileedit-hint'>Comments use the Fossil wiki markup "
|
| ︙ | ︙ | |||
1634 1635 1636 1637 1638 1639 1640 |
previewRenderMode,
"Guess", FE_RENDER_GUESS,
"Wiki/Markdown", FE_RENDER_WIKI,
"HTML (iframe)", FE_RENDER_HTML,
"Plain Text", FE_RENDER_PLAIN_TEXT,
NULL);
/*
| | > | 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 |
previewRenderMode,
"Guess", FE_RENDER_GUESS,
"Wiki/Markdown", FE_RENDER_WIKI,
"HTML (iframe)", FE_RENDER_HTML,
"Plain Text", FE_RENDER_PLAIN_TEXT,
NULL);
/*
** Set up a JS-side mapping of the FE_RENDER_xyz values. This is
** used for dynamically toggling certain UI components on and off.
*/
blob_appendf(&endScript, "fossil.page.previewModes={"
"guess: %d, %d: 'guess', wiki: %d, %d: 'wiki',"
"html: %d, %d: 'html', text: %d, %d: 'text'"
"};\n",
FE_RENDER_GUESS, FE_RENDER_GUESS,
FE_RENDER_WIKI, FE_RENDER_WIKI,
|
| ︙ | ︙ | |||
1689 1690 1691 1692 1693 1694 1695 |
fossil_free(zFileUuid);
if(stmt.pStmt){
db_finalize(&stmt);
}
if(blob_size(&err)){
CX("<div class='fileedit-error-report'>%s</div>",
blob_str(&err));
| < < < | < | 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 |
fossil_free(zFileUuid);
if(stmt.pStmt){
db_finalize(&stmt);
}
if(blob_size(&err)){
CX("<div class='fileedit-error-report'>%s</div>",
blob_str(&err));
}
blob_reset(&err);
CheckinMiniInfo_cleanup(&cimi);
style_emit_script_fetch();
fileedit_emit_page_script();
if(blob_size(&endScript)>0){
style_emit_script_tag(0);
CX("(function(){\n");
CX("try{\n%b\n}catch(e){console.error('Exception:',e)}\n",
&endScript);
CX("})();");
style_emit_script_tag(1);
}
db_end_transaction(0);
style_footer();
}
|
Changes to src/fossil.bootstrap.js.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 |
else{
args.unshift('Fossil status:');
console.debug.apply(console,args);
}
return this;
};
/*
| | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
else{
args.unshift('Fossil status:');
console.debug.apply(console,args);
}
return this;
};
/*
** Set default message.targetElement to #fossil-status-bar, if found.
*/
window.fossil.message.targetElement =
document.querySelector('#fossil-status-bar');
/*
** By default fossil.error() sends its first argument to
** console.error(). If fossil.message.targetElement (yes,
** fossil.message) is set, it adds the 'error' CSS class to
|
| ︙ | ︙ | |||
49 50 51 52 53 54 55 |
}
else{
args.unshift('Fossil error:');
console.error.apply(console,args);
}
return this;
};
| > > > > > > > > > > > > > > > > > > > > > > > > > | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
}
else{
args.unshift('Fossil error:');
console.error.apply(console,args);
}
return this;
};
/**
repoUrl( repoRelativePath [,urlParams] )
Creates a URL by prepending this.rootPath to the given path
(which must be relative from the top of the site, without a
leading slash). If urlParams is a string, it must be
paramters encoded in the form "key=val&key2=val2...", WITHOUT
a leading '?'. If it's an object, all of its properties get
appended to the URL in that form.
*/
window.fossil.repoUrl = function(path,urlParams){
if(!urlParams) return this.rootPath+path;
const url=[this.rootPath,path];
url.push('?');
if('string'===typeof urlParams) url.push(urlParams);
else if('object'===typeof urlParams){
let k, i = 0;
for( k in urlParams ){
if(i++) url.push('&');
url.push(k,'=',encodeURIComponent(urlParams[k]));
}
}
return url.join('');
};
|
Changes to src/fossil.fetch.js.
1 2 3 | "use strict"; /** Documented in style.c:style_emit_script_fetch(). Requires that | | > | > | > | | > | > | > > | < | < < | > | < < < < < < < < < < < < > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
"use strict";
/**
Documented in style.c:style_emit_script_fetch(). Requires that
window.fossil has already been set up (which happens via the routine
which emits this code C-side).
*/
window.fossil.fetch = function f(uri,opt){
if(!f.onerror){
f.onerror = function(e/*event or exception*/){
console.error("Ajax error:",e);
if(e instanceof Error){
fossil.error('Exception:',e);
}
else if(e.originalTarget && e.originalTarget.responseType==='text'){
const txt = e.originalTarget.responseText;
try{
/* The convention from the /filepage_xyz routes is to
** return error responses in JSON form if possible:
** {error: "..."}
*/
const j = JSON.parse(txt);
console.error("Error JSON:",j);
if(j.error){ fossil.error(j.error) };
}catch(e){/* Try harder */
fossil.error(txt)
}
}
};
f.onload = (r)=>console.debug('ajax response:',r);
}
if('/'===uri[0]) uri = uri.substr(1);
if(!opt) opt = {};
else if('function'===typeof opt) opt={onload:opt};
if(!opt.onload) opt.onload = f.onload;
if(!opt.onerror) opt.onerror = f.onerror;
let payload = opt.payload, jsonResponse = false;
if(payload){
opt.method = 'POST';
if(!(payload instanceof FormData)
&& !(payload instanceof Document)
&& !(payload instanceof Blob)
&& !(payload instanceof File)
&& !(payload instanceof ArrayBuffer)){
if('object'===typeof payload || payload instanceof Array){
payload = JSON.stringify(payload);
opt.contentType = 'application/json';
}
}
}
const url=[window.fossil.repoUrl(uri,opt.urlParams)],
x=new XMLHttpRequest();
if('POST'===opt.method && 'string'===typeof opt.contentType){
x.setRequestHeader('Content-Type',opt.contentType);
}
x.open(opt.method||'GET', url.join(''), true);
if('json'===opt.responseType){
/* 'json' is an extension to the supported XHR.responseType
list. We use it as a flag to tell us to JSON.parse()
the response. */
jsonResponse = true;
x.responseType = 'text';
}else{
x.responseType = opt.responseType||'text';
}
if(opt.onload){
x.onload = function(e){
|
| ︙ | ︙ |
Changes to src/fossil.page.fileedit.js.
1 2 3 4 5 6 7 8 9 10 |
(function(){
"use strict";
/**
Code for the /filepage app. Requires that the fossil JS
bootstrapping is complete and fossil.fetch() has been installed.
*/
const E = (s)=>document.querySelector(s);
window.addEventListener("load", function() {
const P = fossil.page;
P.e = {
| | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
(function(){
"use strict";
/**
Code for the /filepage app. Requires that the fossil JS
bootstrapping is complete and fossil.fetch() has been installed.
*/
const E = (s)=>document.querySelector(s);
window.addEventListener("load", function() {
const P = fossil.page;
P.e = {
taEditor: E('#fileedit-content'),
taComment: E('#fileedit-comment'),
ajaxContentTarget: E('#ajax-target'),
form: E('#fileedit-form'),
btnPreview: E("#fileedit-btn-preview"),
btnDiffSbs: E("#fileedit-btn-diffsbs"),
btnDiffU: E("#fileedit-btn-diffu"),
btnCommit: E("#fileedit-btn-commit"),
selectPreviewModeWrap: E('#select-preview-mode'),
|
| ︙ | ︙ | |||
46 47 48 49 50 51 52 |
/**
Cosmetic: jump through some hoops to enable/disable
certain preview options depending on the current
preview mode...
*/
const selectPreviewMode =
P.e.selectPreviewModeWrap.querySelector('select');
| < | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
/**
Cosmetic: jump through some hoops to enable/disable
certain preview options depending on the current
preview mode...
*/
const selectPreviewMode =
P.e.selectPreviewModeWrap.querySelector('select');
selectPreviewMode.addEventListener(
"change",function(e){
const mode = e.target.value,
name = P.previewModes[mode],
hide = [], unhide = [];
if('guess'===name){
unhide.push(P.e.cbLineNumbersWrap,
|
| ︙ | ︙ | |||
68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
hide.forEach((e)=>e.classList.add('hidden'));
unhide.forEach((e)=>e.classList.remove('hidden'));
}, false
);
selectPreviewMode.dispatchEvent(
// Force UI update
new Event('change',{target:selectPreviewMode})
);
}, false);
/**
updateVersion() updates filename and version in relevant UI
| > > > > > > > > > > > > | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
hide.forEach((e)=>e.classList.add('hidden'));
unhide.forEach((e)=>e.classList.remove('hidden'));
}, false
);
selectPreviewMode.dispatchEvent(
// Force UI update
new Event('change',{target:selectPreviewMode})
);
const selectFontSize = E('select[name=editor_font_size]');
selectFontSize.addEventListener(
"change",function(e){
P.e.taEditor.className = e.target.className.replace(
/\bfont-size-\d+/g, '' );
P.e.taEditor.classList.add('font-size-'+e.target.value);
}, false
);
selectFontSize.dispatchEvent(
// Force UI update
new Event('change',{target:selectFontSize})
);
}, false);
/**
updateVersion() updates filename and version in relevant UI
|
| ︙ | ︙ | |||
132 133 134 135 136 137 138 |
Returns this object, noting that the operation is async.
*/
fossil.page.preview = function(){
if(!this.finfo){
fossil.error("No content is loaded.");
return this;
}
| | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
Returns this object, noting that the operation is async.
*/
fossil.page.preview = function(){
if(!this.finfo){
fossil.error("No content is loaded.");
return this;
}
const content = this.e.taEditor.value,
target = this.e.ajaxContentTarget;
const updateView = function(c){
target.innerHTML = [
"<div class='fileedit-preview'>",
"<div>Preview</div>",
c||'',
"</div><!--.fileedit-diff-->"
|
| ︙ | ︙ | |||
174 175 176 177 178 179 180 |
*/
fossil.page.diff = function(sbs){
if(!this.finfo){
fossil.error("No content is loaded.");
return this;
}
const self = this;
| | | 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
*/
fossil.page.diff = function(sbs){
if(!this.finfo){
fossil.error("No content is loaded.");
return this;
}
const self = this;
const content = this.e.taEditor.value,
target = this.e.ajaxContentTarget;
const updateView = function(c){
target.innerHTML = [
"<div class='fileedit-diff'>",
"<div>Diff <code>[",
self.finfo.r,
"]</code> → Local Edits</div>",
|
| ︙ | ︙ | |||
217 218 219 220 221 222 223 |
*/
fossil.page.commit = function f(){
if(!this.finfo){
fossil.error("No content is loaded.");
return this;
}
const self = this;
| | | | > | < > > > > | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
*/
fossil.page.commit = function f(){
if(!this.finfo){
fossil.error("No content is loaded.");
return this;
}
const self = this;
const content = this.e.taEditor.value,
target = this.e.ajaxContentTarget,
cbDryRun = E('[name=dry_run]'),
isDryRun = cbDryRun.checked,
filename = this.finfo.file;
if(!f.updateView){
f.updateView = function(c){
target.innerHTML = [
"<h3>Manifest",
(c.dryRun?" (dry run)":""),
": ", c.uuid.substring(0,16),"</h3>",
"<code class='fileedit-manifest'>",
c.manifest,
"</code></pre>"
].join('');
const msg = [
'Committed',
c.dryRun ? '(dry run)' : '',
'[', c.uuid,'].'
];
if(!c.dryRun){
msg.push('Re-activating dry-run mode.');
self.e.taComment.value = '';
cbDryRun.checked = true;
fossil.page.updateVersion(filename, c.uuid);
}
fossil.message.apply(fossil, msg);
};
}
if(!content){
f.updateView('');
return this;
}
const fd = new FormData();
|
| ︙ | ︙ |
Changes to src/style.c.
| ︙ | ︙ | |||
1422 1423 1424 1425 1426 1427 1428 |
CX("</span>\n");
}
va_end(vargs);
}
/*
| | | 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 |
CX("</span>\n");
}
va_end(vargs);
}
/*
** If passed 0, it emits a script opener tag with this request's
** nonce. If passed non-0 it emits a script closing tag. The very
** first time it is called, it emits some bootstrapping JS code
** immediately after the script opener. Specifically, it defines
** window.fossil if it's not already defined, and may set some
** properties on it.
*/
void style_emit_script_tag(int phase){
|
| ︙ | ︙ | |||
1468 1469 1470 1471 1472 1473 1474 | ** including tags, which defines window.fossil.fetch(), which works ** similarly (not identically) to the not-quite-ubiquitous global ** fetch(). It calls style_emit_script_tag(), which may inject ** other JS bootstrapping bits. ** ** JS usages: ** | | | | | 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 |
** including tags, which defines window.fossil.fetch(), which works
** similarly (not identically) to the not-quite-ubiquitous global
** fetch(). It calls style_emit_script_tag(), which may inject
** other JS bootstrapping bits.
**
** JS usages:
**
** fossil.fetch( URI [, onLoadCallback] );
**
** fossil.fetch( URI [, optionsObject = {}] );
**
** Noting that URI must be relative to the top of the repository and
** should not start with a slash (if it does, it is stripped). It gets
** the equivalent of "%R/" prepended to it.
**
** The optionsObject may be an onload callback or an object with any
** of these properties:
**
** - onload: callback(responseData) (default = output response to
** the console).
**
** - onerror: callback(XHR onload event | exception)
** (default = event or exception to the console).
**
** - method: 'POST' | 'GET' (default = 'GET')
**
** - payload: anything acceptable by XHR2.send(ARG) (DOMString,
** Document, FormData, Blob, File, ArrayBuffer), or a plain object
** or array, either of which gets JSON.stringify()'d. If set then
** the method is automatically set to 'POST'. If an object/array is
|
| ︙ | ︙ | |||
1511 1512 1513 1514 1515 1516 1517 | ** ** - urlParams: string|object. If a string, it is assumed to be a ** URI-encoded list of params in the form "key1=val1&key2=val2...", ** with NO leading '?'. If it is an object, all of its properties ** get converted to that form. Either way, the parameters get ** appended to the URL. ** | > > > > > > > > | | | 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 |
**
** - urlParams: string|object. If a string, it is assumed to be a
** URI-encoded list of params in the form "key1=val1&key2=val2...",
** with NO leading '?'. If it is an object, all of its properties
** get converted to that form. Either way, the parameters get
** appended to the URL.
**
** When an options object does not provide onload() or onerror()
** handlers of its own, this function falls back to
** fossil.fetch.onload() and fossil.fetch.onerror() as defaults. The
** default implementations route the data through the dev console and
** (for onerror()) through fossil.error(). Individual pages may
** overwrite those members to provide default implementations suitable
** for the page's use.
**
** Returns this object, noting that the XHR request is asynchronous,
** and still in transit (or has yet to be sent) when that happens.
*/
void style_emit_script_fetch(){
static int once = 0;
if(0==once){
once = 1;
style_emit_script_tag(0);
CX("%s", builtin_text("fossil.fetch.js"));
style_emit_script_tag(1);
}
}
|