Fossil SCM

fileedit: now gets the file perms via the response header and updates the Is Executable checkbox accordingly. Similary, mimetype is now harvested from the response headers and is used, in place of file extensions, for the "is this a wiki page?" determination. Added fossil-level event infrastructure to allow pages to communicate page-specific events to skin-injected/user-supplied JS code, and fileedit now emits a fileedit-file-loaded event when it loads a new file. Replaced old uses of the term "save" with "commit", per forum feedback.

stephan 2020-05-14 03:00 fileedit-ajaxify
Commit f1b2e509e770b4be664c6b42ae022eab43861f6da244db795eba81ae6de42b07
+15 -6
--- src/fileedit.c
+++ src/fileedit.c
@@ -1207,10 +1207,19 @@
12071207
if(looks_like_binary(&content)){
12081208
zMime = "application/octet-stream";
12091209
}else{
12101210
zMime = "text/plain";
12111211
}
1212
+ }
1213
+ { /* Send the is-exec bit via response header so that the UI can be
1214
+ ** updated to account for that. */
1215
+ int fperm = 0;
1216
+ char * zFuuid = fileedit_file_uuid(zFilename, vid, &fperm);
1217
+ const char * zPerm = mfile_permint_mstring(fperm);
1218
+ assert(zFuuid);
1219
+ cgi_printf_header("x-fileedit-file-perm:%s\r\n", zPerm);
1220
+ fossil_free(zFuuid);
12121221
}
12131222
cgi_set_content_type(zMime);
12141223
cgi_set_content(&content);
12151224
}
12161225
@@ -1271,11 +1280,11 @@
12711280
case FE_RENDER_PLAIN_TEXT: zRenderMode = "text"; break;
12721281
case FE_RENDER_GUESS:
12731282
assert(!"cannot happen");
12741283
}
12751284
if(zRenderMode!=0){
1276
- cgi_printf_header("X-fileedit-render-mode: %s\r\n", zRenderMode);
1285
+ cgi_printf_header("x-fileedit-render-mode: %s\r\n", zRenderMode);
12771286
}
12781287
}
12791288
12801289
/*
12811290
** AJAX route /fileedit?ajax=diff
@@ -1907,18 +1916,18 @@
19071916
{
19081917
/******* Commit flags/options *******/
19091918
CX("<div class='fileedit-options flex-container flex-row'>");
19101919
style_labeled_checkbox("cb-dry-run",
19111920
"dry_run", "Dry-run?", "1", 1,
1912
- "In dry-run mode, the Save button performs "
1913
- "all work needed for saving but then rolls "
1914
- "back the transaction, and thus does not "
1915
- "really save.");
1921
+ "In dry-run mode, the Commit button performs"
1922
+ "all work needed for committing changes but "
1923
+ "then rolls back the transaction, and thus "
1924
+ "does not really commit.");
19161925
style_labeled_checkbox("cb-allow-fork",
19171926
"allow_fork", "Allow fork?", "1",
19181927
cimi.flags & CIMINI_ALLOW_FORK,
1919
- "Allow saving to create a fork?");
1928
+ "Allow committing to create a fork?");
19201929
style_labeled_checkbox("cb-allow-older",
19211930
"allow_older", "Allow older?", "1",
19221931
cimi.flags & CIMINI_ALLOW_OLDER,
19231932
"Allow saving against a parent version "
19241933
"which has a newer timestamp?");
19251934
--- src/fileedit.c
+++ src/fileedit.c
@@ -1207,10 +1207,19 @@
1207 if(looks_like_binary(&content)){
1208 zMime = "application/octet-stream";
1209 }else{
1210 zMime = "text/plain";
1211 }
 
 
 
 
 
 
 
 
 
1212 }
1213 cgi_set_content_type(zMime);
1214 cgi_set_content(&content);
1215 }
1216
@@ -1271,11 +1280,11 @@
1271 case FE_RENDER_PLAIN_TEXT: zRenderMode = "text"; break;
1272 case FE_RENDER_GUESS:
1273 assert(!"cannot happen");
1274 }
1275 if(zRenderMode!=0){
1276 cgi_printf_header("X-fileedit-render-mode: %s\r\n", zRenderMode);
1277 }
1278 }
1279
1280 /*
1281 ** AJAX route /fileedit?ajax=diff
@@ -1907,18 +1916,18 @@
1907 {
1908 /******* Commit flags/options *******/
1909 CX("<div class='fileedit-options flex-container flex-row'>");
1910 style_labeled_checkbox("cb-dry-run",
1911 "dry_run", "Dry-run?", "1", 1,
1912 "In dry-run mode, the Save button performs "
1913 "all work needed for saving but then rolls "
1914 "back the transaction, and thus does not "
1915 "really save.");
1916 style_labeled_checkbox("cb-allow-fork",
1917 "allow_fork", "Allow fork?", "1",
1918 cimi.flags & CIMINI_ALLOW_FORK,
1919 "Allow saving to create a fork?");
1920 style_labeled_checkbox("cb-allow-older",
1921 "allow_older", "Allow older?", "1",
1922 cimi.flags & CIMINI_ALLOW_OLDER,
1923 "Allow saving against a parent version "
1924 "which has a newer timestamp?");
1925
--- src/fileedit.c
+++ src/fileedit.c
@@ -1207,10 +1207,19 @@
1207 if(looks_like_binary(&content)){
1208 zMime = "application/octet-stream";
1209 }else{
1210 zMime = "text/plain";
1211 }
1212 }
1213 { /* Send the is-exec bit via response header so that the UI can be
1214 ** updated to account for that. */
1215 int fperm = 0;
1216 char * zFuuid = fileedit_file_uuid(zFilename, vid, &fperm);
1217 const char * zPerm = mfile_permint_mstring(fperm);
1218 assert(zFuuid);
1219 cgi_printf_header("x-fileedit-file-perm:%s\r\n", zPerm);
1220 fossil_free(zFuuid);
1221 }
1222 cgi_set_content_type(zMime);
1223 cgi_set_content(&content);
1224 }
1225
@@ -1271,11 +1280,11 @@
1280 case FE_RENDER_PLAIN_TEXT: zRenderMode = "text"; break;
1281 case FE_RENDER_GUESS:
1282 assert(!"cannot happen");
1283 }
1284 if(zRenderMode!=0){
1285 cgi_printf_header("x-fileedit-render-mode: %s\r\n", zRenderMode);
1286 }
1287 }
1288
1289 /*
1290 ** AJAX route /fileedit?ajax=diff
@@ -1907,18 +1916,18 @@
1916 {
1917 /******* Commit flags/options *******/
1918 CX("<div class='fileedit-options flex-container flex-row'>");
1919 style_labeled_checkbox("cb-dry-run",
1920 "dry_run", "Dry-run?", "1", 1,
1921 "In dry-run mode, the Commit button performs"
1922 "all work needed for committing changes but "
1923 "then rolls back the transaction, and thus "
1924 "does not really commit.");
1925 style_labeled_checkbox("cb-allow-fork",
1926 "allow_fork", "Allow fork?", "1",
1927 cimi.flags & CIMINI_ALLOW_FORK,
1928 "Allow committing to create a fork?");
1929 style_labeled_checkbox("cb-allow-older",
1930 "allow_older", "Allow older?", "1",
1931 cimi.flags & CIMINI_ALLOW_OLDER,
1932 "Allow saving against a parent version "
1933 "which has a newer timestamp?");
1934
--- src/fossil.bootstrap.js
+++ src/fossil.bootstrap.js
@@ -274,7 +274,49 @@
274274
}, false
275275
);
276276
});
277277
return this;
278278
};
279
+
280
+ /**
281
+ Adds a listener for fossil-level custom events. Events are
282
+ delivered to their callbacks as CustomEvent objects with a
283
+ 'detail' property holding the event's app-level data.
284
+
285
+ The exact events fired differ by page, and not all pages trigger
286
+ events.
287
+
288
+ Pedantic sidebar: the custom event's 'target' property is an
289
+ unspecified DOM element. Clients must not rely on its value being
290
+ anything specific or useful.
291
+
292
+ Returns this object.
293
+ */
294
+ F.page.addEventListener = function f(eventName, callback){
295
+ if(!f.proxy){
296
+ f.proxy = document.createElement('span');
297
+ }
298
+ f.proxy.addEventListener(eventName, callback, false);
299
+ return this;
300
+ };
301
+
302
+ /**
303
+ Internal. Dispatches a new CustomEvent to all listeners
304
+ registered for the given eventName via
305
+ fossil.page.addEventListener(), passing on a new CustomEvent
306
+ with a 'detail' property equal to the 2nd argument's
307
+ value. Returns this object.
308
+ */
309
+ F.page.dispatchEvent = function(eventName, eventDetail){
310
+ if(this.addEventListener.proxy){
311
+ try{
312
+ this.addEventListener.proxy.dispatchEvent(
313
+ new CustomEvent(eventName,{detail: eventDetail})
314
+ );
315
+ }catch(e){
316
+ console.error(eventName,"event listener threw:",e);
317
+ }
318
+ }
319
+ return this;
320
+ };
279321
280322
})(window);
281323
--- src/fossil.bootstrap.js
+++ src/fossil.bootstrap.js
@@ -274,7 +274,49 @@
274 }, false
275 );
276 });
277 return this;
278 };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
280 })(window);
281
--- src/fossil.bootstrap.js
+++ src/fossil.bootstrap.js
@@ -274,7 +274,49 @@
274 }, false
275 );
276 });
277 return this;
278 };
279
280 /**
281 Adds a listener for fossil-level custom events. Events are
282 delivered to their callbacks as CustomEvent objects with a
283 'detail' property holding the event's app-level data.
284
285 The exact events fired differ by page, and not all pages trigger
286 events.
287
288 Pedantic sidebar: the custom event's 'target' property is an
289 unspecified DOM element. Clients must not rely on its value being
290 anything specific or useful.
291
292 Returns this object.
293 */
294 F.page.addEventListener = function f(eventName, callback){
295 if(!f.proxy){
296 f.proxy = document.createElement('span');
297 }
298 f.proxy.addEventListener(eventName, callback, false);
299 return this;
300 };
301
302 /**
303 Internal. Dispatches a new CustomEvent to all listeners
304 registered for the given eventName via
305 fossil.page.addEventListener(), passing on a new CustomEvent
306 with a 'detail' property equal to the 2nd argument's
307 value. Returns this object.
308 */
309 F.page.dispatchEvent = function(eventName, eventDetail){
310 if(this.addEventListener.proxy){
311 try{
312 this.addEventListener.proxy.dispatchEvent(
313 new CustomEvent(eventName,{detail: eventDetail})
314 );
315 }catch(e){
316 console.error(eventName,"event listener threw:",e);
317 }
318 }
319 return this;
320 };
321
322 })(window);
323
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -1,10 +1,30 @@
11
(function(F/*the fossil object*/){
22
"use strict";
33
/**
44
Code for the /filepage app. Requires that the fossil JS
55
bootstrapping is complete and fossil.fetch() has been installed.
6
+
7
+ Custom events, handled via fossil.page.addEventListener():
8
+
9
+ - 'fileedit-file-loaded': passes on information when it loads a
10
+ file, in the form of an object:
11
+
12
+ {
13
+ filename: string,
14
+ checkin: UUID string,
15
+ isExe: bool,
16
+ mimetype: mimetype stringas determined by the fossil server.
17
+ }
18
+
19
+ The fossil.page.value() method gets or sets the current file
20
+ content for the page. Hypothetically, this can be overridden by
21
+ skin-level JS in order to use a custom 3rd-party editing widget
22
+ in place of the built-in textarea, but that is as yet untested.
23
+ In order to do so the client would need to replace DOM element
24
+ #fileedit-content-editor with their custom widget.
25
+
626
*/
727
const E = (s)=>document.querySelector(s),
828
D = F.dom,
929
P = F.page;
1030
@@ -187,10 +207,11 @@
187207
selectPreviewMode: E('#select-preview-mode select'),
188208
selectHtmlEmsWrap: E('#select-preview-html-ems'),
189209
selectEolWrap: E('#select-preview-html-ems'),
190210
cbLineNumbersWrap: E('#cb-line-numbers'),
191211
cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'),
212
+ cbIsExe: E('input[type=checkbox][name=exec_bit]'),
192213
tabs:{
193214
content: E('#fileedit-tab-content'),
194215
preview: E('#fileedit-tab-preview'),
195216
diff: E('#fileedit-tab-diff'),
196217
commit: E('#fileedit-tab-commit')
@@ -299,20 +320,29 @@
299320
selectFontSize.dispatchEvent(
300321
// Force UI update
301322
new Event('change',{target:selectFontSize})
302323
);
303324
}
325
+
326
+ if(0){ // only for testing
327
+ P.addEventListener(
328
+ 'fileedit-file-loaded',
329
+ (e)=>console.debug('fileedit-file-loaded ==>',e)
330
+ );
331
+ }
332
+
304333
}, false)/*onload event handler*/;
305334
306335
/**
307336
Getter (if called with no args) or setter (if passed an arg) for
308337
the current file content. We use a function, rather than direct
309
- access so that clients can hypothetically swap out this method
310
- from their skin in order to facilitate plugging-in of fancy
311
- 3rd-party editor widgets.
338
+ access, so that clients can hypothetically swap out this method
339
+ from their skin in order to facilitate plugging-in of a fancy
340
+ 3rd-party editor widget.
312341
313
- The setter form returns this object.
342
+ The setter form returns this object, and re-implementations must
343
+ do the same.
314344
*/
315345
P.value = function(){
316346
if(0===arguments.length){
317347
return this.e.taEditor.value;
318348
}else{
@@ -324,12 +354,12 @@
324354
/**
325355
If either of...
326356
327357
- P.previewModes.current==='wiki'
328358
329
- - P.previewModes.current==='guess' AND the currently-loaded
330
- file has an extension of (md|wiki)
359
+ - P.previewModes.current==='guess' AND the currently-loaded file
360
+ has a mimetype of "text/x-fossil-wiki" or "text/x-markdown".
331361
332362
... then this function updates the document's base.href to a
333363
repo-relative /doc/{{this.finfo.checkin}}/{{directory part of
334364
this.finfo.filename}}/
335365
@@ -336,16 +366,16 @@
336366
If neither of those conditions applies, this is a no-op.
337367
*/
338368
P.baseHrefForFile = function f(){
339369
const fn = this.finfo ? this.finfo.filename : undefined;
340370
if(!fn) return this;
341
- if(!f.rxWiki){
342
- f.rxWiki = /\.(wiki|md)$/i;
371
+ if(!f.wikiMimeTypes){
372
+ f.wikiMimeTypes = ["text/x-fossil-wiki", "text/x-markdown"];
343373
}
344374
if('wiki'===P.previewModes.current
345375
|| ('guess'===P.previewModes.current
346
- && f.rxWiki.test(fn))){
376
+ && f.wikiMimeTypes.indexOf(this.finfo.mimetype)>=0)){
347377
const a = fn.split('/');
348378
a.pop();
349379
this.base.tag.href = F.repoUrl(
350380
'doc/'+F.hashDigits(this.finfo.checkin)
351381
+'/'+(a.length ? a.join('/')+'/' : '')
@@ -436,13 +466,17 @@
436466
return !!P.finfo;
437467
};
438468
439469
/**
440470
loadFile() loads (file,checkinVersion) and updates the relevant
441
- UI elements to reflect the loaded state.
471
+ UI elements to reflect the loaded state. If passed no arguments
472
+ then it re-uses the values from the currently-loaded file
473
+ (becoming a no-op if no file is loaded).
442474
443
- Returns this object, noting that the load is async.
475
+ Returns this object, noting that the load is async. After loading
476
+ it triggers a 'fileedit-file-loaded' event, passing it
477
+ this.finfo.
444478
*/
445479
P.loadFile = function(file,rev){
446480
if(0===arguments.length){
447481
if(!affirmHasFile()) return this;
448482
file = this.finfo.filename;
@@ -455,15 +489,20 @@
455489
urlParams: {
456490
ajax: 'content',
457491
filename:file,
458492
checkin:rev
459493
},
460
- onload:(r)=>{
494
+ responseHeaders: ['x-fileedit-file-perm', 'content-type'],
495
+ onload:(r,headers)=>{
461496
F.message('Loaded content.');
462
- self.value(r);
463497
self.updateVersion(file,rev);
498
+ self.finfo.isExe = ('x'===headers['x-fileedit-file-perm']);
499
+ self.finfo.mimetype = headers['content-type'].split(';').shift();
464500
self.tabs.switchToTab(self.e.tabs.content);
501
+ self.e.cbIsExe.checked = self.finfo.isExe;
502
+ self.value(r);
503
+ self.dispatchEvent('fileedit-file-loaded', self.finfo);
465504
}
466505
});
467506
return this;
468507
};
469508
@@ -524,14 +563,13 @@
524563
}
525564
});
526565
return this;
527566
};
528567
529
-
530568
/**
531
- Fetches the content diff based on the contents and settings of this
532
- page's input fields, and updates the UI with the diff view.
569
+ Fetches the content diff based on the contents and settings of
570
+ this page's input fields, and updates the UI with the diff view.
533571
534572
Returns this object, noting that the operation is async.
535573
*/
536574
P.diff = function f(sbs){
537575
if(!affirmHasFile()) return this;
@@ -644,8 +682,7 @@
644682
responseType: 'json',
645683
onload: f.updateView
646684
});
647685
return this;
648686
};
649
-
650687
651688
})(window.fossil);
652689
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -1,10 +1,30 @@
1 (function(F/*the fossil object*/){
2 "use strict";
3 /**
4 Code for the /filepage app. Requires that the fossil JS
5 bootstrapping is complete and fossil.fetch() has been installed.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6 */
7 const E = (s)=>document.querySelector(s),
8 D = F.dom,
9 P = F.page;
10
@@ -187,10 +207,11 @@
187 selectPreviewMode: E('#select-preview-mode select'),
188 selectHtmlEmsWrap: E('#select-preview-html-ems'),
189 selectEolWrap: E('#select-preview-html-ems'),
190 cbLineNumbersWrap: E('#cb-line-numbers'),
191 cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'),
 
192 tabs:{
193 content: E('#fileedit-tab-content'),
194 preview: E('#fileedit-tab-preview'),
195 diff: E('#fileedit-tab-diff'),
196 commit: E('#fileedit-tab-commit')
@@ -299,20 +320,29 @@
299 selectFontSize.dispatchEvent(
300 // Force UI update
301 new Event('change',{target:selectFontSize})
302 );
303 }
 
 
 
 
 
 
 
 
304 }, false)/*onload event handler*/;
305
306 /**
307 Getter (if called with no args) or setter (if passed an arg) for
308 the current file content. We use a function, rather than direct
309 access so that clients can hypothetically swap out this method
310 from their skin in order to facilitate plugging-in of fancy
311 3rd-party editor widgets.
312
313 The setter form returns this object.
 
314 */
315 P.value = function(){
316 if(0===arguments.length){
317 return this.e.taEditor.value;
318 }else{
@@ -324,12 +354,12 @@
324 /**
325 If either of...
326
327 - P.previewModes.current==='wiki'
328
329 - P.previewModes.current==='guess' AND the currently-loaded
330 file has an extension of (md|wiki)
331
332 ... then this function updates the document's base.href to a
333 repo-relative /doc/{{this.finfo.checkin}}/{{directory part of
334 this.finfo.filename}}/
335
@@ -336,16 +366,16 @@
336 If neither of those conditions applies, this is a no-op.
337 */
338 P.baseHrefForFile = function f(){
339 const fn = this.finfo ? this.finfo.filename : undefined;
340 if(!fn) return this;
341 if(!f.rxWiki){
342 f.rxWiki = /\.(wiki|md)$/i;
343 }
344 if('wiki'===P.previewModes.current
345 || ('guess'===P.previewModes.current
346 && f.rxWiki.test(fn))){
347 const a = fn.split('/');
348 a.pop();
349 this.base.tag.href = F.repoUrl(
350 'doc/'+F.hashDigits(this.finfo.checkin)
351 +'/'+(a.length ? a.join('/')+'/' : '')
@@ -436,13 +466,17 @@
436 return !!P.finfo;
437 };
438
439 /**
440 loadFile() loads (file,checkinVersion) and updates the relevant
441 UI elements to reflect the loaded state.
 
 
442
443 Returns this object, noting that the load is async.
 
 
444 */
445 P.loadFile = function(file,rev){
446 if(0===arguments.length){
447 if(!affirmHasFile()) return this;
448 file = this.finfo.filename;
@@ -455,15 +489,20 @@
455 urlParams: {
456 ajax: 'content',
457 filename:file,
458 checkin:rev
459 },
460 onload:(r)=>{
 
461 F.message('Loaded content.');
462 self.value(r);
463 self.updateVersion(file,rev);
 
 
464 self.tabs.switchToTab(self.e.tabs.content);
 
 
 
465 }
466 });
467 return this;
468 };
469
@@ -524,14 +563,13 @@
524 }
525 });
526 return this;
527 };
528
529
530 /**
531 Fetches the content diff based on the contents and settings of this
532 page's input fields, and updates the UI with the diff view.
533
534 Returns this object, noting that the operation is async.
535 */
536 P.diff = function f(sbs){
537 if(!affirmHasFile()) return this;
@@ -644,8 +682,7 @@
644 responseType: 'json',
645 onload: f.updateView
646 });
647 return this;
648 };
649
650
651 })(window.fossil);
652
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -1,10 +1,30 @@
1 (function(F/*the fossil object*/){
2 "use strict";
3 /**
4 Code for the /filepage app. Requires that the fossil JS
5 bootstrapping is complete and fossil.fetch() has been installed.
6
7 Custom events, handled via fossil.page.addEventListener():
8
9 - 'fileedit-file-loaded': passes on information when it loads a
10 file, in the form of an object:
11
12 {
13 filename: string,
14 checkin: UUID string,
15 isExe: bool,
16 mimetype: mimetype stringas determined by the fossil server.
17 }
18
19 The fossil.page.value() method gets or sets the current file
20 content for the page. Hypothetically, this can be overridden by
21 skin-level JS in order to use a custom 3rd-party editing widget
22 in place of the built-in textarea, but that is as yet untested.
23 In order to do so the client would need to replace DOM element
24 #fileedit-content-editor with their custom widget.
25
26 */
27 const E = (s)=>document.querySelector(s),
28 D = F.dom,
29 P = F.page;
30
@@ -187,10 +207,11 @@
207 selectPreviewMode: E('#select-preview-mode select'),
208 selectHtmlEmsWrap: E('#select-preview-html-ems'),
209 selectEolWrap: E('#select-preview-html-ems'),
210 cbLineNumbersWrap: E('#cb-line-numbers'),
211 cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'),
212 cbIsExe: E('input[type=checkbox][name=exec_bit]'),
213 tabs:{
214 content: E('#fileedit-tab-content'),
215 preview: E('#fileedit-tab-preview'),
216 diff: E('#fileedit-tab-diff'),
217 commit: E('#fileedit-tab-commit')
@@ -299,20 +320,29 @@
320 selectFontSize.dispatchEvent(
321 // Force UI update
322 new Event('change',{target:selectFontSize})
323 );
324 }
325
326 if(0){ // only for testing
327 P.addEventListener(
328 'fileedit-file-loaded',
329 (e)=>console.debug('fileedit-file-loaded ==>',e)
330 );
331 }
332
333 }, false)/*onload event handler*/;
334
335 /**
336 Getter (if called with no args) or setter (if passed an arg) for
337 the current file content. We use a function, rather than direct
338 access, so that clients can hypothetically swap out this method
339 from their skin in order to facilitate plugging-in of a fancy
340 3rd-party editor widget.
341
342 The setter form returns this object, and re-implementations must
343 do the same.
344 */
345 P.value = function(){
346 if(0===arguments.length){
347 return this.e.taEditor.value;
348 }else{
@@ -324,12 +354,12 @@
354 /**
355 If either of...
356
357 - P.previewModes.current==='wiki'
358
359 - P.previewModes.current==='guess' AND the currently-loaded file
360 has a mimetype of "text/x-fossil-wiki" or "text/x-markdown".
361
362 ... then this function updates the document's base.href to a
363 repo-relative /doc/{{this.finfo.checkin}}/{{directory part of
364 this.finfo.filename}}/
365
@@ -336,16 +366,16 @@
366 If neither of those conditions applies, this is a no-op.
367 */
368 P.baseHrefForFile = function f(){
369 const fn = this.finfo ? this.finfo.filename : undefined;
370 if(!fn) return this;
371 if(!f.wikiMimeTypes){
372 f.wikiMimeTypes = ["text/x-fossil-wiki", "text/x-markdown"];
373 }
374 if('wiki'===P.previewModes.current
375 || ('guess'===P.previewModes.current
376 && f.wikiMimeTypes.indexOf(this.finfo.mimetype)>=0)){
377 const a = fn.split('/');
378 a.pop();
379 this.base.tag.href = F.repoUrl(
380 'doc/'+F.hashDigits(this.finfo.checkin)
381 +'/'+(a.length ? a.join('/')+'/' : '')
@@ -436,13 +466,17 @@
466 return !!P.finfo;
467 };
468
469 /**
470 loadFile() loads (file,checkinVersion) and updates the relevant
471 UI elements to reflect the loaded state. If passed no arguments
472 then it re-uses the values from the currently-loaded file
473 (becoming a no-op if no file is loaded).
474
475 Returns this object, noting that the load is async. After loading
476 it triggers a 'fileedit-file-loaded' event, passing it
477 this.finfo.
478 */
479 P.loadFile = function(file,rev){
480 if(0===arguments.length){
481 if(!affirmHasFile()) return this;
482 file = this.finfo.filename;
@@ -455,15 +489,20 @@
489 urlParams: {
490 ajax: 'content',
491 filename:file,
492 checkin:rev
493 },
494 responseHeaders: ['x-fileedit-file-perm', 'content-type'],
495 onload:(r,headers)=>{
496 F.message('Loaded content.');
 
497 self.updateVersion(file,rev);
498 self.finfo.isExe = ('x'===headers['x-fileedit-file-perm']);
499 self.finfo.mimetype = headers['content-type'].split(';').shift();
500 self.tabs.switchToTab(self.e.tabs.content);
501 self.e.cbIsExe.checked = self.finfo.isExe;
502 self.value(r);
503 self.dispatchEvent('fileedit-file-loaded', self.finfo);
504 }
505 });
506 return this;
507 };
508
@@ -524,14 +563,13 @@
563 }
564 });
565 return this;
566 };
567
 
568 /**
569 Fetches the content diff based on the contents and settings of
570 this page's input fields, and updates the UI with the diff view.
571
572 Returns this object, noting that the operation is async.
573 */
574 P.diff = function f(sbs){
575 if(!affirmHasFile()) return this;
@@ -644,8 +682,7 @@
682 responseType: 'json',
683 onload: f.updateView
684 });
685 return this;
686 };
 
687
688 })(window.fossil);
689

Keyboard Shortcuts

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