Fossil SCM

Merge recent changes from trunk.

drh 2020-08-20 16:25 sec2020 merge
Commit c93cb2bae9504846e85051ad0c40e4ac9516d5a2927a73cbb73cfaaa96d083b2
+1 -1
--- src/diff.c
+++ src/diff.c
@@ -1566,11 +1566,11 @@
15661566
iSYp = iSY;
15671567
iEXp = iEX;
15681568
iEYp = iEY;
15691569
}
15701570
}
1571
- if( iSXb==iEXb && (iE1-iS1)*(iE2-iS2)<400 ){
1571
+ if( iSXb==iEXb && (sqlite3_int64)(iE1-iS1)*(iE2-iS2)<400 ){
15721572
/* If no common sequence is found using the hashing heuristic and
15731573
** the input is not too big, use the expensive exact solution */
15741574
optimalLCS(p, iS1, iE1, iS2, iE2, piSX, piEX, piSY, piEY);
15751575
}else{
15761576
*piSX = iSXb;
15771577
--- src/diff.c
+++ src/diff.c
@@ -1566,11 +1566,11 @@
1566 iSYp = iSY;
1567 iEXp = iEX;
1568 iEYp = iEY;
1569 }
1570 }
1571 if( iSXb==iEXb && (iE1-iS1)*(iE2-iS2)<400 ){
1572 /* If no common sequence is found using the hashing heuristic and
1573 ** the input is not too big, use the expensive exact solution */
1574 optimalLCS(p, iS1, iE1, iS2, iE2, piSX, piEX, piSY, piEY);
1575 }else{
1576 *piSX = iSXb;
1577
--- src/diff.c
+++ src/diff.c
@@ -1566,11 +1566,11 @@
1566 iSYp = iSY;
1567 iEXp = iEX;
1568 iEYp = iEY;
1569 }
1570 }
1571 if( iSXb==iEXb && (sqlite3_int64)(iE1-iS1)*(iE2-iS2)<400 ){
1572 /* If no common sequence is found using the hashing heuristic and
1573 ** the input is not too big, use the expensive exact solution */
1574 optimalLCS(p, iS1, iE1, iS2, iE2, piSX, piEX, piSY, piEY);
1575 }else{
1576 *piSX = iSXb;
1577
--- src/fossil.numbered-lines.js
+++ src/fossil.numbered-lines.js
@@ -18,11 +18,14 @@
1818
}
1919
}
2020
const F = window.fossil, D = F.dom;
2121
const tdLn = tbl.querySelector('td.line-numbers');
2222
const lineState = {
23
- urlArgs: (window.location.search||'?').replace(/&?\bln=[^&]*/,''),
23
+ urlArgs: (window.location.search||'?')
24
+ .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
25
+ .replace(/&?\bln=[^&]*/,'') /* inbound line number/range */
26
+ .replace('?&','?'),
2427
start: 0, end: 0
2528
};
2629
2730
const lineTip = new fossil.PopupWidget({
2831
style: {
@@ -38,12 +41,13 @@
3841
window.location.toString().split('?')[0]
3942
+ lineState.urlArgs + '&ln='+ls.join('-')
4043
);
4144
D.append(
4245
D.clearElement(link),
43
- ' ',
44
- (ls.length===1 ? 'line ' : 'lines ')+ls.join('-')
46
+ ' Copy link to '+(
47
+ ls.length===1 ? 'line ' : 'lines '
48
+ )+ls.join('-')
4549
);
4650
}else{
4751
D.append(link, "No lines selected.");
4852
}
4953
},
@@ -55,11 +59,11 @@
5559
F.copyButton(btnCopy,{
5660
copyFromElement: link,
5761
extractText: ()=>link.dataset.url,
5862
oncopy: (ev)=>{
5963
D.flashOnce(ev.target, undefined, ()=>lineTip.hide());
60
- F.toast.message("Copied link to clipboard.");
64
+ // arguably too snazzy: F.toast.message("Copied link to clipboard.");
6165
}
6266
});
6367
this.e.addEventListener('click', ()=>btnCopy.click(), false);
6468
D.append(this.e, btnCopy, link)
6569
}
6670
--- src/fossil.numbered-lines.js
+++ src/fossil.numbered-lines.js
@@ -18,11 +18,14 @@
18 }
19 }
20 const F = window.fossil, D = F.dom;
21 const tdLn = tbl.querySelector('td.line-numbers');
22 const lineState = {
23 urlArgs: (window.location.search||'?').replace(/&?\bln=[^&]*/,''),
 
 
 
24 start: 0, end: 0
25 };
26
27 const lineTip = new fossil.PopupWidget({
28 style: {
@@ -38,12 +41,13 @@
38 window.location.toString().split('?')[0]
39 + lineState.urlArgs + '&ln='+ls.join('-')
40 );
41 D.append(
42 D.clearElement(link),
43 ' ',
44 (ls.length===1 ? 'line ' : 'lines ')+ls.join('-')
 
45 );
46 }else{
47 D.append(link, "No lines selected.");
48 }
49 },
@@ -55,11 +59,11 @@
55 F.copyButton(btnCopy,{
56 copyFromElement: link,
57 extractText: ()=>link.dataset.url,
58 oncopy: (ev)=>{
59 D.flashOnce(ev.target, undefined, ()=>lineTip.hide());
60 F.toast.message("Copied link to clipboard.");
61 }
62 });
63 this.e.addEventListener('click', ()=>btnCopy.click(), false);
64 D.append(this.e, btnCopy, link)
65 }
66
--- src/fossil.numbered-lines.js
+++ src/fossil.numbered-lines.js
@@ -18,11 +18,14 @@
18 }
19 }
20 const F = window.fossil, D = F.dom;
21 const tdLn = tbl.querySelector('td.line-numbers');
22 const lineState = {
23 urlArgs: (window.location.search||'?')
24 .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
25 .replace(/&?\bln=[^&]*/,'') /* inbound line number/range */
26 .replace('?&','?'),
27 start: 0, end: 0
28 };
29
30 const lineTip = new fossil.PopupWidget({
31 style: {
@@ -38,12 +41,13 @@
41 window.location.toString().split('?')[0]
42 + lineState.urlArgs + '&ln='+ls.join('-')
43 );
44 D.append(
45 D.clearElement(link),
46 ' Copy link to '+(
47 ls.length===1 ? 'line ' : 'lines '
48 )+ls.join('-')
49 );
50 }else{
51 D.append(link, "No lines selected.");
52 }
53 },
@@ -55,11 +59,11 @@
59 F.copyButton(btnCopy,{
60 copyFromElement: link,
61 extractText: ()=>link.dataset.url,
62 oncopy: (ev)=>{
63 D.flashOnce(ev.target, undefined, ()=>lineTip.hide());
64 // arguably too snazzy: F.toast.message("Copied link to clipboard.");
65 }
66 });
67 this.e.addEventListener('click', ()=>btnCopy.click(), false);
68 D.append(this.e, btnCopy, link)
69 }
70
--- src/fossil.storage.js
+++ src/fossil.storage.js
@@ -21,42 +21,90 @@
2121
const $storage =
2222
tryStorage(window.localStorage)
2323
|| tryStorage(window.sessionStorage)
2424
|| tryStorage({
2525
// A basic dummy xyzStorage stand-in
26
- $:{},
27
- setItem: function(k,v){this.$[k]=v},
26
+ $$$:{},
27
+ setItem: function(k,v){this.$$$[k]=v},
2828
getItem: function(k){
29
- return this.$.hasOwnProperty(k) ? this.$[k] : undefined;
29
+ return this.$$$.hasOwnProperty(k) ? this.$$$[k] : undefined;
3030
},
31
- removeItem: function(k){delete this.$[k]},
32
- clear: function(){this.$={}}
31
+ removeItem: function(k){delete this.$$$[k]},
32
+ clear: function(){this.$$$={}}
3333
});
3434
3535
/**
3636
For the dummy storage we need to differentiate between
3737
$storage and its real property storage for hasOwnProperty()
3838
to work properly...
3939
*/
40
- const $storageHolder = $storage.hasOwnProperty('$') ? $storage.$ : $storage;
40
+ const $storageHolder = $storage.hasOwnProperty('$$$') ? $storage.$$$ : $storage;
41
+
42
+ /**
43
+ A prefix which gets internally applied to all fossil.storage
44
+ property keys so that localStorage and sessionStorage across the
45
+ same browser profile instance do not "leak" across multiple repos
46
+ being hosted by the same origin server. Such polination is still
47
+ there but, with this key prefix applied, it won't be immediately
48
+ visible via the storage API.
49
+
50
+ With this in place we can justify using localStorage instead of
51
+ sessionStorage again.
52
+
53
+ One implication, it was discovered after the release of 2.12, of
54
+ using localStorage and sessionStorage, is that their scope (the
55
+ same "origin" and client application/profile) allows multiple
56
+ repos on the same origin to use the same storage. Thus a user
57
+ editing a wiki in /repoA/wikiedit could then see those edits in
58
+ /repoB/wikiedit. The data do not cross user- or browser
59
+ boundaries, though, so it "might" arguably be called a bug. Even
60
+ so, it was never intended for that to happen. Rather than lose
61
+ localStorage access altogether, storageKeyPrefix was added so
62
+ that we can sandbox that state for the various repos.
63
+
64
+ See: https://fossil-scm.org/forum/forumpost/4afc4d34de
65
+
66
+ Sidebar: it might seem odd to provide a key prefix and stick all
67
+ properties in the topmost level of the storage object. We do that
68
+ because adding a layer of object to sandbox each repo would mean
69
+ (de)serializing that whole tree on every storage property change
70
+ (and we update storage often during editing
71
+ sessions). e.g. instead of storageObject.projectName.foo we have
72
+ storageObject[storageKeyPrefix+'foo']. That's soley for
73
+ efficiency's sake (in terms of battery life and
74
+ environment-internal storage-level effort). Even so, it might
75
+ (or might not) be useful to do that someday.
76
+ */
77
+ const storageKeyPrefix = (
78
+ $storageHolder===$storage/*localStorage or sessionStorage*/
79
+ ? (
80
+ F.config.projectCode || F.config.projectName
81
+ || F.config.shortProjectName || window.location.pathname
82
+ )+'::' : (
83
+ '' /* transient storage */
84
+ )
85
+ );
4186
4287
/**
4388
A proxy for localStorage or sessionStorage or a
4489
page-instance-local proxy, if neither one is availble.
4590
4691
Which exact storage implementation is uses is unspecified, and
4792
apps must not rely on it.
4893
*/
4994
fossil.storage = {
95
+ storageKeyPrefix: storageKeyPrefix,
5096
/** Sets the storage key k to value v, implicitly converting
5197
it to a string. */
52
- set: (k,v)=>$storage.setItem(k,v),
98
+ set: (k,v)=>$storage.setItem(storageKeyPrefix+k,v),
5399
/** Sets storage key k to JSON.stringify(v). */
54
- setJSON: (k,v)=>$storage.setItem(k,JSON.stringify(v)),
100
+ setJSON: (k,v)=>$storage.setItem(storageKeyPrefix+k,JSON.stringify(v)),
55101
/** Returns the value for the given storage key, or
56102
dflt if the key is not found in the storage. */
57
- get: (k,dflt)=>$storageHolder.hasOwnProperty(k) ? $storage.getItem(k) : dflt,
103
+ get: (k,dflt)=>$storageHolder.hasOwnProperty(
104
+ storageKeyPrefix+k
105
+ ) ? $storage.getItem(storageKeyPrefix+k) : dflt,
58106
/** Returns the JSON.parse()'d value of the given
59107
storage key's value, or dflt is the key is not
60108
found or JSON.parse() fails. */
61109
getJSON: function f(k,dflt){
62110
try {
@@ -65,23 +113,23 @@
65113
}
66114
catch(e){return dflt}
67115
},
68116
/** Returns true if the storage contains the given key,
69117
else false. */
70
- contains: (k)=>$storageHolder.hasOwnProperty(k),
118
+ contains: (k)=>$storageHolder.hasOwnProperty(storageKeyPrefix+k),
71119
/** Removes the given key from the storage. Returns this. */
72120
remove: function(k){
73
- $storage.removeItem(k);
121
+ $storage.removeItem(storageKeyPrefix+k);
74122
return this;
75123
},
76124
/** Clears ALL keys from the storage. Returns this. */
77125
clear: function(){
78
- $storage.clear();
126
+ this.keys().forEach((k)=>$storage.removeItem(/*w/o prefix*/k));
79127
return this;
80128
},
81129
/** Returns an array of all keys currently in the storage. */
82
- keys: ()=>Object.keys($storageHolder),
130
+ keys: ()=>Object.keys($storageHolder).filter((v)=>(v||'').startsWith(storageKeyPrefix)),
83131
/** Returns true if this storage is transient (only available
84132
until the page is reloaded), indicating that fileStorage
85133
and sessionStorage are unavailable. */
86134
isTransient: ()=>$storageHolder!==$storage,
87135
/** Returns a symbolic name for the current storage mechanism. */
88136
--- src/fossil.storage.js
+++ src/fossil.storage.js
@@ -21,42 +21,90 @@
21 const $storage =
22 tryStorage(window.localStorage)
23 || tryStorage(window.sessionStorage)
24 || tryStorage({
25 // A basic dummy xyzStorage stand-in
26 $:{},
27 setItem: function(k,v){this.$[k]=v},
28 getItem: function(k){
29 return this.$.hasOwnProperty(k) ? this.$[k] : undefined;
30 },
31 removeItem: function(k){delete this.$[k]},
32 clear: function(){this.$={}}
33 });
34
35 /**
36 For the dummy storage we need to differentiate between
37 $storage and its real property storage for hasOwnProperty()
38 to work properly...
39 */
40 const $storageHolder = $storage.hasOwnProperty('$') ? $storage.$ : $storage;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
42 /**
43 A proxy for localStorage or sessionStorage or a
44 page-instance-local proxy, if neither one is availble.
45
46 Which exact storage implementation is uses is unspecified, and
47 apps must not rely on it.
48 */
49 fossil.storage = {
 
50 /** Sets the storage key k to value v, implicitly converting
51 it to a string. */
52 set: (k,v)=>$storage.setItem(k,v),
53 /** Sets storage key k to JSON.stringify(v). */
54 setJSON: (k,v)=>$storage.setItem(k,JSON.stringify(v)),
55 /** Returns the value for the given storage key, or
56 dflt if the key is not found in the storage. */
57 get: (k,dflt)=>$storageHolder.hasOwnProperty(k) ? $storage.getItem(k) : dflt,
 
 
58 /** Returns the JSON.parse()'d value of the given
59 storage key's value, or dflt is the key is not
60 found or JSON.parse() fails. */
61 getJSON: function f(k,dflt){
62 try {
@@ -65,23 +113,23 @@
65 }
66 catch(e){return dflt}
67 },
68 /** Returns true if the storage contains the given key,
69 else false. */
70 contains: (k)=>$storageHolder.hasOwnProperty(k),
71 /** Removes the given key from the storage. Returns this. */
72 remove: function(k){
73 $storage.removeItem(k);
74 return this;
75 },
76 /** Clears ALL keys from the storage. Returns this. */
77 clear: function(){
78 $storage.clear();
79 return this;
80 },
81 /** Returns an array of all keys currently in the storage. */
82 keys: ()=>Object.keys($storageHolder),
83 /** Returns true if this storage is transient (only available
84 until the page is reloaded), indicating that fileStorage
85 and sessionStorage are unavailable. */
86 isTransient: ()=>$storageHolder!==$storage,
87 /** Returns a symbolic name for the current storage mechanism. */
88
--- src/fossil.storage.js
+++ src/fossil.storage.js
@@ -21,42 +21,90 @@
21 const $storage =
22 tryStorage(window.localStorage)
23 || tryStorage(window.sessionStorage)
24 || tryStorage({
25 // A basic dummy xyzStorage stand-in
26 $$$:{},
27 setItem: function(k,v){this.$$$[k]=v},
28 getItem: function(k){
29 return this.$$$.hasOwnProperty(k) ? this.$$$[k] : undefined;
30 },
31 removeItem: function(k){delete this.$$$[k]},
32 clear: function(){this.$$$={}}
33 });
34
35 /**
36 For the dummy storage we need to differentiate between
37 $storage and its real property storage for hasOwnProperty()
38 to work properly...
39 */
40 const $storageHolder = $storage.hasOwnProperty('$$$') ? $storage.$$$ : $storage;
41
42 /**
43 A prefix which gets internally applied to all fossil.storage
44 property keys so that localStorage and sessionStorage across the
45 same browser profile instance do not "leak" across multiple repos
46 being hosted by the same origin server. Such polination is still
47 there but, with this key prefix applied, it won't be immediately
48 visible via the storage API.
49
50 With this in place we can justify using localStorage instead of
51 sessionStorage again.
52
53 One implication, it was discovered after the release of 2.12, of
54 using localStorage and sessionStorage, is that their scope (the
55 same "origin" and client application/profile) allows multiple
56 repos on the same origin to use the same storage. Thus a user
57 editing a wiki in /repoA/wikiedit could then see those edits in
58 /repoB/wikiedit. The data do not cross user- or browser
59 boundaries, though, so it "might" arguably be called a bug. Even
60 so, it was never intended for that to happen. Rather than lose
61 localStorage access altogether, storageKeyPrefix was added so
62 that we can sandbox that state for the various repos.
63
64 See: https://fossil-scm.org/forum/forumpost/4afc4d34de
65
66 Sidebar: it might seem odd to provide a key prefix and stick all
67 properties in the topmost level of the storage object. We do that
68 because adding a layer of object to sandbox each repo would mean
69 (de)serializing that whole tree on every storage property change
70 (and we update storage often during editing
71 sessions). e.g. instead of storageObject.projectName.foo we have
72 storageObject[storageKeyPrefix+'foo']. That's soley for
73 efficiency's sake (in terms of battery life and
74 environment-internal storage-level effort). Even so, it might
75 (or might not) be useful to do that someday.
76 */
77 const storageKeyPrefix = (
78 $storageHolder===$storage/*localStorage or sessionStorage*/
79 ? (
80 F.config.projectCode || F.config.projectName
81 || F.config.shortProjectName || window.location.pathname
82 )+'::' : (
83 '' /* transient storage */
84 )
85 );
86
87 /**
88 A proxy for localStorage or sessionStorage or a
89 page-instance-local proxy, if neither one is availble.
90
91 Which exact storage implementation is uses is unspecified, and
92 apps must not rely on it.
93 */
94 fossil.storage = {
95 storageKeyPrefix: storageKeyPrefix,
96 /** Sets the storage key k to value v, implicitly converting
97 it to a string. */
98 set: (k,v)=>$storage.setItem(storageKeyPrefix+k,v),
99 /** Sets storage key k to JSON.stringify(v). */
100 setJSON: (k,v)=>$storage.setItem(storageKeyPrefix+k,JSON.stringify(v)),
101 /** Returns the value for the given storage key, or
102 dflt if the key is not found in the storage. */
103 get: (k,dflt)=>$storageHolder.hasOwnProperty(
104 storageKeyPrefix+k
105 ) ? $storage.getItem(storageKeyPrefix+k) : dflt,
106 /** Returns the JSON.parse()'d value of the given
107 storage key's value, or dflt is the key is not
108 found or JSON.parse() fails. */
109 getJSON: function f(k,dflt){
110 try {
@@ -65,23 +113,23 @@
113 }
114 catch(e){return dflt}
115 },
116 /** Returns true if the storage contains the given key,
117 else false. */
118 contains: (k)=>$storageHolder.hasOwnProperty(storageKeyPrefix+k),
119 /** Removes the given key from the storage. Returns this. */
120 remove: function(k){
121 $storage.removeItem(storageKeyPrefix+k);
122 return this;
123 },
124 /** Clears ALL keys from the storage. Returns this. */
125 clear: function(){
126 this.keys().forEach((k)=>$storage.removeItem(/*w/o prefix*/k));
127 return this;
128 },
129 /** Returns an array of all keys currently in the storage. */
130 keys: ()=>Object.keys($storageHolder).filter((v)=>(v||'').startsWith(storageKeyPrefix)),
131 /** Returns true if this storage is transient (only available
132 until the page is reloaded), indicating that fileStorage
133 and sessionStorage are unavailable. */
134 isTransient: ()=>$storageHolder!==$storage,
135 /** Returns a symbolic name for the current storage mechanism. */
136
+1 -1
--- src/markdown.c
+++ src/markdown.c
@@ -1919,11 +1919,11 @@
19191919
19201920
/* fallback on default alignment if not explicit */
19211921
if( align==0 && aligns && col<align_size ) align = aligns[col];
19221922
19231923
/* render cells */
1924
- if( cells && end>beg ){
1924
+ if( cells && end>=beg ){
19251925
parse_table_cell(cells, rndr, data+beg, end-beg, align|flags);
19261926
}
19271927
19281928
col++;
19291929
}
19301930
--- src/markdown.c
+++ src/markdown.c
@@ -1919,11 +1919,11 @@
1919
1920 /* fallback on default alignment if not explicit */
1921 if( align==0 && aligns && col<align_size ) align = aligns[col];
1922
1923 /* render cells */
1924 if( cells && end>beg ){
1925 parse_table_cell(cells, rndr, data+beg, end-beg, align|flags);
1926 }
1927
1928 col++;
1929 }
1930
--- src/markdown.c
+++ src/markdown.c
@@ -1919,11 +1919,11 @@
1919
1920 /* fallback on default alignment if not explicit */
1921 if( align==0 && aligns && col<align_size ) align = aligns[col];
1922
1923 /* render cells */
1924 if( cells && end>=beg ){
1925 parse_table_cell(cells, rndr, data+beg, end-beg, align|flags);
1926 }
1927
1928 col++;
1929 }
1930
+2 -2
--- src/stat.c
+++ src/stat.c
@@ -153,11 +153,11 @@
153153
fsize = file_size(g.zRepositoryName, ExtFILE);
154154
@ <tr><th>Repository&nbsp;Size:</th><td>%,lld(fsize) bytes</td>
155155
@ </td></tr>
156156
if( !brief ){
157157
@ <tr><th>Number&nbsp;Of&nbsp;Artifacts:</th><td>
158
- n = db_int(0, "SELECT count(*) FROM blob");
158
+ n = db_int(0, "SELECT count(*) FROM blob WHERE content IS NOT NULL");
159159
m = db_int(0, "SELECT count(*) FROM delta");
160160
@ %.d(n) (%,d(n-m) fulltext and %,d(m) deltas)
161161
if( g.perm.Write ){
162162
@ <a href='%R/artifact_stats'>Details</a>
163163
}
@@ -165,11 +165,11 @@
165165
if( n>0 ){
166166
int a, b;
167167
Stmt q;
168168
@ <tr><th>Uncompressed&nbsp;Artifact&nbsp;Size:</th><td>
169169
db_prepare(&q, "SELECT total(size), avg(size), max(size)"
170
- " FROM blob WHERE size>0 /*scan*/");
170
+ " FROM blob WHERE content IS NOT NULL /*scan*/");
171171
db_step(&q);
172172
t = db_column_int64(&q, 0);
173173
szAvg = db_column_int(&q, 1);
174174
szMax = db_column_int(&q, 2);
175175
db_finalize(&q);
176176
--- src/stat.c
+++ src/stat.c
@@ -153,11 +153,11 @@
153 fsize = file_size(g.zRepositoryName, ExtFILE);
154 @ <tr><th>Repository&nbsp;Size:</th><td>%,lld(fsize) bytes</td>
155 @ </td></tr>
156 if( !brief ){
157 @ <tr><th>Number&nbsp;Of&nbsp;Artifacts:</th><td>
158 n = db_int(0, "SELECT count(*) FROM blob");
159 m = db_int(0, "SELECT count(*) FROM delta");
160 @ %.d(n) (%,d(n-m) fulltext and %,d(m) deltas)
161 if( g.perm.Write ){
162 @ <a href='%R/artifact_stats'>Details</a>
163 }
@@ -165,11 +165,11 @@
165 if( n>0 ){
166 int a, b;
167 Stmt q;
168 @ <tr><th>Uncompressed&nbsp;Artifact&nbsp;Size:</th><td>
169 db_prepare(&q, "SELECT total(size), avg(size), max(size)"
170 " FROM blob WHERE size>0 /*scan*/");
171 db_step(&q);
172 t = db_column_int64(&q, 0);
173 szAvg = db_column_int(&q, 1);
174 szMax = db_column_int(&q, 2);
175 db_finalize(&q);
176
--- src/stat.c
+++ src/stat.c
@@ -153,11 +153,11 @@
153 fsize = file_size(g.zRepositoryName, ExtFILE);
154 @ <tr><th>Repository&nbsp;Size:</th><td>%,lld(fsize) bytes</td>
155 @ </td></tr>
156 if( !brief ){
157 @ <tr><th>Number&nbsp;Of&nbsp;Artifacts:</th><td>
158 n = db_int(0, "SELECT count(*) FROM blob WHERE content IS NOT NULL");
159 m = db_int(0, "SELECT count(*) FROM delta");
160 @ %.d(n) (%,d(n-m) fulltext and %,d(m) deltas)
161 if( g.perm.Write ){
162 @ <a href='%R/artifact_stats'>Details</a>
163 }
@@ -165,11 +165,11 @@
165 if( n>0 ){
166 int a, b;
167 Stmt q;
168 @ <tr><th>Uncompressed&nbsp;Artifact&nbsp;Size:</th><td>
169 db_prepare(&q, "SELECT total(size), avg(size), max(size)"
170 " FROM blob WHERE content IS NOT NULL /*scan*/");
171 db_step(&q);
172 t = db_column_int64(&q, 0);
173 szAvg = db_column_int(&q, 1);
174 szMax = db_column_int(&q, 2);
175 db_finalize(&q);
176
+10
--- src/style.c
+++ src/style.c
@@ -1436,10 +1436,11 @@
14361436
** 2) Emits the static fossil.bootstrap.js using builtin_request_js().
14371437
*/
14381438
void style_emit_script_fossil_bootstrap(int addScriptTag){
14391439
static int once = 0;
14401440
if(0==once++){
1441
+ char * zName;
14411442
/* Set up the generic/app-agnostic parts of window.fossil
14421443
** which require C-level state... */
14431444
if(addScriptTag!=0){
14441445
style_emit_script_tag(0,0);
14451446
}
@@ -1456,10 +1457,19 @@
14561457
** including a trailing slash. */
14571458
"window.fossil.rootPath = %!j+'/';\n",
14581459
get_version(), g.zTop);
14591460
/* fossil.config = {...various config-level options...} */
14601461
CX("window.fossil.config = {");
1462
+ zName = db_get("project-name", "");
1463
+ CX("projectName: %!j,\n", zName);
1464
+ fossil_free(zName);
1465
+ zName = db_get("short-project-name", "");
1466
+ CX("shortProjectName: %!j,\n", zName);
1467
+ fossil_free(zName);
1468
+ zName = db_get("project-code", "");
1469
+ CX("projectCode: %!j,\n", zName);
1470
+ fossil_free(zName);
14611471
CX("/* Length of UUID hashes for display purposes. */");
14621472
CX("hashDigits: %d, hashDigitsUrl: %d,\n",
14631473
hash_digits(0), hash_digits(1));
14641474
CX("editStateMarkers: {"
14651475
"/*Symbolic markers to denote certain edit states.*/"
14661476
--- src/style.c
+++ src/style.c
@@ -1436,10 +1436,11 @@
1436 ** 2) Emits the static fossil.bootstrap.js using builtin_request_js().
1437 */
1438 void style_emit_script_fossil_bootstrap(int addScriptTag){
1439 static int once = 0;
1440 if(0==once++){
 
1441 /* Set up the generic/app-agnostic parts of window.fossil
1442 ** which require C-level state... */
1443 if(addScriptTag!=0){
1444 style_emit_script_tag(0,0);
1445 }
@@ -1456,10 +1457,19 @@
1456 ** including a trailing slash. */
1457 "window.fossil.rootPath = %!j+'/';\n",
1458 get_version(), g.zTop);
1459 /* fossil.config = {...various config-level options...} */
1460 CX("window.fossil.config = {");
 
 
 
 
 
 
 
 
 
1461 CX("/* Length of UUID hashes for display purposes. */");
1462 CX("hashDigits: %d, hashDigitsUrl: %d,\n",
1463 hash_digits(0), hash_digits(1));
1464 CX("editStateMarkers: {"
1465 "/*Symbolic markers to denote certain edit states.*/"
1466
--- src/style.c
+++ src/style.c
@@ -1436,10 +1436,11 @@
1436 ** 2) Emits the static fossil.bootstrap.js using builtin_request_js().
1437 */
1438 void style_emit_script_fossil_bootstrap(int addScriptTag){
1439 static int once = 0;
1440 if(0==once++){
1441 char * zName;
1442 /* Set up the generic/app-agnostic parts of window.fossil
1443 ** which require C-level state... */
1444 if(addScriptTag!=0){
1445 style_emit_script_tag(0,0);
1446 }
@@ -1456,10 +1457,19 @@
1457 ** including a trailing slash. */
1458 "window.fossil.rootPath = %!j+'/';\n",
1459 get_version(), g.zTop);
1460 /* fossil.config = {...various config-level options...} */
1461 CX("window.fossil.config = {");
1462 zName = db_get("project-name", "");
1463 CX("projectName: %!j,\n", zName);
1464 fossil_free(zName);
1465 zName = db_get("short-project-name", "");
1466 CX("shortProjectName: %!j,\n", zName);
1467 fossil_free(zName);
1468 zName = db_get("project-code", "");
1469 CX("projectCode: %!j,\n", zName);
1470 fossil_free(zName);
1471 CX("/* Length of UUID hashes for display purposes. */");
1472 CX("hashDigits: %d, hashDigitsUrl: %d,\n",
1473 hash_digits(0), hash_digits(1));
1474 CX("editStateMarkers: {"
1475 "/*Symbolic markers to denote certain edit states.*/"
1476
+443 -129
--- www/javascript.md
+++ www/javascript.md
@@ -1,28 +1,28 @@
11
# Use of JavaScript in Fossil
22
3
-## Philosophy
3
+## Philosophy & Policy
44
55
The Fossil development project’s policy is to use JavaScript where it
66
helps make its web UI better, but to offer graceful fallbacks wherever
7
-practical. The intent is that the UI be usable with JavaScript entirely
8
-disabled. In every place where Fossil uses JavaScript, it is an
9
-enhancement to provided functionality, and there is always another way
10
-to accomplish a given end without using JavaScript.
7
+practical. The intent is that the UI be usable with JavaScript
8
+entirely disabled. In almost all places where Fossil uses JavaScript,
9
+it is an enhancement to provided functionality, and there is always
10
+another way to accomplish a given end without using JavaScript.
1111
1212
This is not to say that Fossil’s fall-backs for such cases are always as
1313
elegant and functional as a no-JS purist might wish. That is simply
14
-because [the vast majority of web users run with JS enabled](#stats),
15
-and a minority of those run with some kind of conditional JavaScript
16
-blocking in place. Fossil’s active developers do not deviate from that
14
+because [the vast majority of web users run with JavaScript enabled](#stats),
15
+and a minority of those run with some kind of [conditional JavaScript
16
+blocking](#block) in place. Fossil’s active developers do not deviate from that
1717
norm enough that we have many no-JS purists among us, so the no-JS case
1818
doesn’t get as much attention as some might want. We do [accept code
1919
contributions][cg], and we are philosophically in favor of graceful
2020
fall-backs, so you are welcome to appoint yourself the position of no-JS
2121
czar for the Fossil project!
2222
23
-Evil is in actions, not in nouns, so we do not believe JavaScript *can*
23
+Evil is in actions, not in nouns: we do not believe JavaScript *can*
2424
be evil. It is an active technology, but the actions that matter here
2525
are those of writing the code and checking it into the Fossil project
2626
repository. None of the JavaScript code in Fossil is evil, a fact we
2727
enforce by being careful about who we give check-in rights on the
2828
repository to and by policing what code does get contributed. The Fossil
@@ -30,98 +30,264 @@
3030
3131
We think it’s better to ask not whether Fossil requires JavaScript but
3232
whether Fossil uses JavaScript *well*, so that [you can decide](#block)
3333
to block or allow Fossil’s use of JavaScript.
3434
35
+The Fossil developers want to see the project thrive, and we achieve
36
+that best by making it usable and friendly to a wider audience than the
37
+minority of static web app purists. Modern users generally expect a
38
+smoother experience than was available with 1990s style HTTP
39
+POST-and-response `<form>` based interaction. We also increase the set
40
+of potential Fossil developers if we do not restrict them to such
41
+antiquated methods.
42
+
43
+JavaScript is not perfect, but it's what we have, so we will use it
44
+where we find it advantageous.
45
+
3546
[cg]: ./contribute.wiki
3647
3748
38
-## <a id="block"></a>Blocking JavaScript
39
-
40
-Rather than either block JavaScript wholesale or give up on blocking
41
-JavaScript entirely, we recommend that you use tools like [NoScript][ns]
42
-or [uBlock Origin][ub] to selectively block problematic uses of
43
-JavaScript so the rest of the web can use the technology productively,
44
-as it was intended. There are doubtless other useful tools of this sort;
45
-we recommend only these two due to our limited experience, not out of
46
-any wish to exclude other tools.
47
-
48
-The primary difference between these two for our purposes is that
49
-NoScript lets you select scripts to run on a page on a case-by-case
50
-basis, whereas uBlock Origin delegates those choices to a group of
51
-motivated volunteers who maintain whitelists and blacklists to control
52
-all of this; you can then override UBO’s stock rules as needed.
53
-
54
-[ns]: https://noscript.net/
55
-[ub]: https://github.com/gorhill/uBlock/
56
-
57
-
58
-## <a id="stats"></a>How Many Users Run with JavaScript Disabled Anyway?
59
-
60
-There are several studies that have directly measured the web audience
61
-to answer this question:
62
-
63
-* [What percentage of browsers with javascript disabled?][s1]
64
-* [How many people are missing out on JavaScript enhancement?][s2]
65
-* [Just how many web users really disable cookies or JavaScript?][s3]
66
-
67
-Our sense of this data is that only about 0.2% of web users had
68
-JavaScript disabled while participating in these studies.
69
-
70
-The Fossil user community is not typical of the wider web, but if we
71
-were able to comprehensively survey our users, we’d expect to find an
72
-interesting dichotomy. Because Fossil is targeted at software
73
-developers, who in turn are more likely to be power-users, we’d expect
74
-to find Fossil users to be more in favor of some amount of JavaScript
75
-blocking than the average web user. Yet, we’d also expect to find that
76
-our user base has a disproportionately high number who run [powerful
77
-conditional blocking plugins](#block) in their browsers, rather than
78
-block JS entirely. We suspect that between these two forces, the number
79
-of no-JS purists among Fossil’s user base is still a tiny minority.
80
-
81
-[s1]: https://blockmetry.com/blog/javascript-disabled
82
-[s2]: https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/
83
-[s3]: https://w3techs.com/technologies/overview/client_side_language/all
84
-
85
-
86
-## <a id="3pjs"></a>No Third-Party JavaScript in Fossil
87
-
88
-Fossil does not use any third-party JavaScript libraries, not even very
89
-common ones like jQuery. Every bit of JavaScript served by the stock
90
-version of Fossil was written specifically for the Fossil project and is
91
-stored [in its code repository](https://fossil-scm.org/fossil/file).
92
-
93
-Therefore, if you want to hack on the JavaScript code served by Fossil
94
-and mechanisms like [skin editing][cs] don’t suffice for your purposes,
95
-you can hack on the JavaScript in your local instance directly, just as
96
-you can hack on its C, SQL, and Tcl code. Fossil is free and open source
97
-software, under [a single license][2cbsd].
98
-
99
-[2cbsd]: https://fossil-scm.org/home/doc/trunk/COPYRIGHT-BSD2.txt
100
-[cs]: ./customskin.md
101
-
102
-
103
-## <a id="snoop"></a>Fossil Does Not Snoop On You
104
-
105
-There is no tracking or other snooping technology in Fossil other than
106
-that necessary for basic security, such as IP address logging on
107
-check-ins. (This is in part why we have no [comprehensive user
108
-statistics](#stats)!)
109
-
110
-Fossil attempts to set two cookies on all web clients: a login session
111
-cookie and a display preferences cookie. These cookies are restricted to
112
-the Fossil instance, so even this limited data cannot leak between
113
-Fossil instances or into other web sites.
114
-
115
-There is some server-side event logging, but that is done entirely
116
-without JavaScript, so it’s off-topic here.
117
-
49
+## <a id="debate"></a>Arguments Against JavaScript & Our Rebuttals
50
+
51
+There are many common arguments against the use of JavaScript. Rather than
52
+rehash these same arguments on the [forum][ffor], we distill the common
53
+ones we’ve heard before and give our stock answers to them here:
54
+
55
+1. “**It increases the size of the page download.**”
56
+
57
+ The heaviest such pages served by Fossil only have about 8 kB of
58
+ compressed JavaScript. (You have to go out of your way to get Fossil
59
+ to serve uncompressed pages.) This is negligible, even over very
60
+ slow data connections. If you are still somehow on a 56 kbit/sec
61
+ analog telephone modem, this extra script code would download in
62
+ about a second.
63
+
64
+ Most JavaScript-based Fossil pages use less code than that.
65
+
66
+ Atop that, Fossil 2.12 adds new script delivery methods with
67
+ aggressive caching enabled so that typical page loads will skip
68
+ re-loading this content on subsequent loads. These features are
69
+ currently optional: you must either set the new [`fossil server
70
+ --jsmode` option][fsrv] or the corresponding `jsmode` control line
71
+ in your [`fossil cgi`][fcgi] script when setting up your
72
+ [Fossil server][fshome]. That done, Fossil’s JavaScript files will
73
+ load almost instantly from the browser’s cache after the initial
74
+ page load, rather than be re-transferred over the network.
75
+
76
+ Between the improved caching and the fact that it’s quicker to
77
+ transfer a partial Ajax page load than reload the entire page, the
78
+ aggregate cost of such pages is typically *lower* than the older
79
+ methods based on HTTP POST with a full server round-trip. You can
80
+ expect to recover the cost of the initial page load in 1-2
81
+ round-trips. If we were to double the amount of JavaScript code in
82
+ Fossil, the payoff time would increase to 2-4 round-trips.
83
+
84
+2. “**JavaScript is slow.**”
85
+
86
+ It *was*, before September 2008. Google's introduction of [their V8
87
+ JavaScript engine][v8] taught the world that JavaScript need not be
88
+ slow. This competitive pressure caused the other common JavaScript
89
+ interpreters to either improve or be replaced by one of the engines
90
+ that did improve to approach V8’s speed.
91
+
92
+ Nowadays JavaScript is, as a rule, astoundingly fast. As the world
93
+ continues to move more and more to web-based applications and
94
+ services, JavaScript engine developers have ample motivation to keep
95
+ their engines fast and competitive.
96
+
97
+ Once the scripts are cached, Ajax based page updates are faster than
98
+ the alternative, a full HTTP POST round-trip.
99
+
100
+3. <a id="3pjs"></a>“**Third-party JavaScript cannot be trusted.**”
101
+
102
+ Fossil does not use any third-party JavaScript libraries, not even
103
+ very common ones like jQuery. Every bit of JavaScript served by the
104
+ stock version of Fossil was written specifically for the Fossil
105
+ project and is stored [in its code repository][fsrc].
106
+
107
+ Therefore, if you want to hack on the JavaScript code served by
108
+ Fossil and mechanisms like [skin editing][cskin] don’t suffice for your
109
+ purposes, you can hack on the JavaScript in your local instance
110
+ directly, just as you can hack on its C, SQL, and Tcl code. Fossil
111
+ is free and open source software, under [a single license][2cbsd].
112
+
113
+4. <a id="snoop"></a>“**JavaScript and cookies are used to snoop on web users.**”
114
+
115
+ There is no tracking or other snooping technology in Fossil other than
116
+ that necessary for basic security, such as IP address logging on
117
+ check-ins. (This is in part why we have no [comprehensive user
118
+ statistics](#stats)!)
119
+
120
+ Fossil attempts to set two cookies on all web clients: a login session
121
+ cookie and a display preferences cookie. These cookies are restricted to
122
+ the Fossil instance, so even this limited data cannot leak between
123
+ Fossil instances or into other web sites.
124
+
125
+5. “**JavaScript is fundamentally insecure.**”
126
+
127
+ JavaScript is certainly sometimes used for nefarious ends, but if we
128
+ wish to have more features in Fossil, the alternative is to add more
129
+ code to the Fossil binary, [most likely in C][fslpl], a language
130
+ implicated in [over 4× more security vulnerabilities][whmsl].
131
+
132
+ Therefore, does it not make sense to place approximately four times
133
+ as much trust in Fossil’s JavaScript code as in its C code?
134
+
135
+ The question is not whether JavaScript is itself evil, it is whether
136
+ its *authors* are evil. *Every byte* of JavaScript code used within
137
+ the Fossil UI is:
138
+
139
+ * ...written by the Fossil developers, vetted by their peers.
140
+
141
+ * ...[open source][flic] and [available][fsrc] to be inspected,
142
+ audited, and changed by its users.
143
+
144
+ * ...compiled directly into the `fossil` binary in a
145
+ non-obfuscated form during the build process, so there are no
146
+ third-party servers delivering mysterious, obfuscated JavaScript
147
+ code blobs to the user.
148
+
149
+ Local administrators can [modify the repository’s skin][cskin] to
150
+ inject additional JavaScript code into pages served by their Fossil
151
+ server. A typical case is to add a syntax highlighter like
152
+ [Prism.js][pjs] or [highlightjs][hljs] to the local repository. At
153
+ that point, your trust concern is not with Fossil’s use of
154
+ JavaScript, but with your trust in that repository’s administrator.
155
+
156
+ Fossil's [default content security policy][dcsp] (CSP)
157
+ prohibits execution of JavaScript code which is delivered from
158
+ anywhere but the Fossil server which delivers the page. A local
159
+ administrator can change this CSP, but again this comes down to a
160
+ matter of trust with the administrator, not with Fossil itself.
161
+
162
+6. “**Cross-browser compatibility is poor.**”
163
+
164
+ It most certainly was in the first decade or so of JavaScript’s
165
+ lifetime, resulting in the creation of powerful libraries like
166
+ jQuery to patch over the incompatibilities. Over time, the need for
167
+ such libraries has dropped as browser vendors have fixed the
168
+ incompatibilities. Cross-browser JavaScript compatibility issues
169
+ which affect web developers are, by and large, a thing of the past.
170
+
171
+7. “**Fossil UI works fine today without JavaScript. Why break it?**”
172
+
173
+ While this is true today, and we have no philosophical objection to
174
+ it remaining true, we do not intend to limit ourselves to only those
175
+ features that can be created without JavaScript. The mere
176
+ availability of alternatives is not a good justification for holding
177
+ back on notable improvements when they're within easy reach.
178
+
179
+ The no-JS case is a [minority position](#stats), so those that want
180
+ Fossil to have no-JS alternatives and graceful fallbacks will need
181
+ to get involved with the development if they want this state of
182
+ affairs to continue.
183
+
184
+8. <a id="stats"></a>“**A large number of users run without JavaScript enabled.**”
185
+
186
+ That’s not what web audience measurements say:
187
+
188
+ * [What percentage of browsers with javascript disabled?][s1]
189
+ * [How many people are missing out on JavaScript enhancement?][s2]
190
+ * [Just how many web users really disable cookies or JavaScript?][s3]
191
+
192
+ Our sense of this data is that only about 0.2% of web users had
193
+ JavaScript disabled while participating in these studies.
194
+
195
+ The Fossil user community is not typical of the wider web, but if we
196
+ were able to comprehensively survey our users, we’d expect to find
197
+ an interesting dichotomy. Because Fossil is targeted at software
198
+ developers, who in turn are more likely to be power-users, we’d
199
+ expect to find Fossil users to be more in favor of some amount of
200
+ JavaScript blocking than the average web user. Yet, we’d also expect
201
+ to find that our user base has a disproportionately high number who
202
+ run [powerful conditional blocking plugins](#block) in their
203
+ browsers, rather than block JavaScript entirely. We suspect that
204
+ between these two forces, the number of no-JS purists among Fossil’s
205
+ user base is still a tiny minority.
206
+
207
+9. <a id="block"></a>“**I block JavaScript entirely in my browser. That breaks Fossil.**”
208
+
209
+ First, see our philosophy statements above. Briefly, we intend that
210
+ there always be some other way to get any given result without using
211
+ JavaScript, developer interest willing.
212
+
213
+ But second, it doesn’t have to be all-or-nothing. We recommend that
214
+ those interested in blocking problematic uses of JavaScript use
215
+ tools like [NoScript][ns] or [uBlock Origin][ubo] to *selectively*
216
+ block JavaScript so the rest of the web can use the technology
217
+ productively, as it was intended.
218
+
219
+ There are doubtless other useful tools of this sort. We recommend
220
+ these two only from our limited experience, not out of any wish to
221
+ exclude other tools.
222
+
223
+ The primary difference between these two for our purposes is that
224
+ NoScript lets you select scripts to run on a page on a case-by-case
225
+ basis, whereas uBlock Origin delegates those choices to a group of
226
+ motivated volunteers who maintain allow/block lists to control all
227
+ of this; you can then override UBO’s stock rules as needed.
228
+
229
+10. “**My browser doesn’t even *have* a JavaScript interpreter.**”
230
+
231
+ The Fossil open source project has no full-time developers, and only
232
+ a few of these part-timers are responsible for the bulk of the code
233
+ in Fossil. If you want Fossil to support such niche use cases, then
234
+ you will have to [get involved with its development][cg]: it’s
235
+ *your* uncommon itch.
236
+
237
+11. <a id="compat"></a>“**Fossil’s JavaScript code isn’t compatible with my browser.**”
238
+
239
+ The Fossil project’s developers aim to remain compatible with
240
+ the largest portions of the client-side browser base. We use only
241
+ standards-defined JavaScript features which are known to work in the
242
+ overwhelmingly vast majority of browsers going back approximately 5
243
+ years, at minimum, as documented by [Can I Use...?][ciu] We avoid use of
244
+ features added to the language more recently or those which are still in
245
+ flux in standards committees.
246
+
247
+ We set this threshold based on the amount of time it typically takes for
248
+ new standards to propagate through the installed base.
249
+
250
+ As of this writing, this means we are only using features defined in
251
+ [ECMAScript 2015][es2015], colloquially called “JavaScript 6.” That
252
+ is a sufficiently rich standard that it more than suffices for our
253
+ purposes, and it is [widely deployed][es6dep]. The biggest single
254
+ outlier remaining is MSIE 11, and [even Microsoft is moving their
255
+ own products off of it][ie11x].
256
+
257
+[2cbsd]: https://fossil-scm.org/home/doc/trunk/COPYRIGHT-BSD2.txt
258
+[ciu]: https://caniuse.com/
259
+[cskin]: ./customskin.md
260
+[dcsp]: ./defcsp.md
261
+[es2015]: https://ecma-international.org/ecma-262/6.0/
262
+[es6dep]: https://caniuse.com/#feat=es6
263
+[fcgi]: /help?cmd=cgi
264
+[ffor]: https://fossil-scm.org/forum/
265
+[flic]: /doc/trunk/COPYRIGHT-BSD2.txt
266
+[fshome]: /doc/trunk/www/server/
267
+[fslpl]: /doc/trunk/www/fossil-v-git.wiki#portable
268
+[fsrc]: https://fossil-scm.org/home/file/src
269
+[fsrv]: /help?cmd=server
270
+[hljs]: https://fossil-scm.org/forum/forumpost/9150bc22ca
271
+[ie11x]: https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666
272
+[ns]: https://noscript.net/
273
+[pjs]: https://fossil-scm.org/forum/forumpost/1198651c6d
274
+[s1]: https://blockmetry.com/blog/javascript-disabled
275
+[s2]: https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/
276
+[s3]: https://w3techs.com/technologies/overview/client_side_language/all
277
+[ubo]: https://github.com/gorhill/uBlock/
278
+[v8]: https://en.wikipedia.org/wiki/V8_(JavaScript_engine)
279
+[whmsl]: https://www.whitesourcesoftware.com/most-secure-programming-languages/
280
+
281
+
282
+----
118283
119284
## <a id="uses"></a>Places Where Fossil’s Web UI Uses JavaScript
120285
121
-The remainder of this document will explain how Fossil currently uses
122
-JavaScript and what it does when these uses are blocked.
286
+This section documents the areas where Fossil currently uses JavaScript
287
+and what it does when these uses are blocked. It also gives common
288
+workarounds where necessary.
123289
124290
125291
### <a id="timeline"></a>Timeline Graph
126292
127293
Fossil’s [web timeline][wt] uses JavaScript to render the graph
@@ -140,45 +306,144 @@
140306
command, by clicking around within the web UI, etc.
141307
142308
_Potential Workaround:_ The timeline could be enhanced with `<noscript>`
143309
tags that replace the graph with a column of checkboxes that control
144310
what a series of form submit buttons do when clicked, replicating the
145
-current JS-based features of the graph using client-server round-trips.
311
+current JavaScript-based features of the graph using client-server round-trips.
146312
For example, you could click two of those checkboxes and then a button
147313
labeled “Diff Selected” to replicate the current “click two nodes to
148314
diff them” feature.
149315
150316
[wt]: https://fossil-scm.org/fossil/timeline
151317
152318
153
-### <a id="wedit"></a>WYSIWYG Wiki Editor
154
-
155
-The Admin → Wiki → “Enable WYSIWYG Wiki Editing” toggle switches the
156
-default plaintext editor for [Fossil wiki][fw] documents to one that
157
-works like a basic word processor. This feature requires JavaScript in
158
-order to react to editor button clicks like the “**B**” button, meaning
159
-“make \[selected\] text boldface.” There is no standard WYSIWYG editor
160
-component in browsers, doubtless because it’s relatively straightforward
161
-to create one using JavaScript.
162
-
163
-_Graceful Fallback:_ Edit your wiki documents in the default plain text
164
-wiki editor. Fossil’s wiki and Markdown language processors were
165
-designed to be edited that way.
166
-
167
-[fw]: ./wikitheory.wiki
319
+### <a id="wedit"></a>The New Wiki Editor
320
+
321
+The [new wiki editor][fwt] added in Fossil 2.12 has many new features, a
322
+few of which are impossible to get without use of JavaScript.
323
+
324
+First, it allows in-browser previews without losing client-side editor
325
+state, such as where your cursor is. With the old editor, you had to
326
+re-locate the place you were last editing on each preview, which would
327
+reduce the incentive to use the preview function. In the new wiki
328
+editor, you just click the Preview tab to see how Fossil interprets your
329
+markup, then click back to the Editor tab to resume work with the prior
330
+context undisturbed.
331
+
332
+Second, it continually saves your document state in client-side storage
333
+in the background while you’re editing it so that if the browser closes
334
+without saving the changes back to the Fossil repository, you can resume
335
+editing from the stored copy without losing work. This feature is not so
336
+much about saving you from crashes of various sorts, since computers are
337
+so much more reliable these days. It is far more likely to save you from
338
+the features of mobile OSes like Android and iOS which aggressively shut
339
+down and restart apps to save on RAM. That OS design philosophy assumes
340
+that there is a way for the app to restore its prior state from
341
+persistent media when it’s restarted, giving the illusion that it was
342
+never shut down in the first place. This feature of Fossil’s new wiki
343
+editor provides that.
344
+
345
+With this change, we lost the old WYSIWYG wiki editor, available since
346
+Fossil version 1.24. It hadn’t been maintained for years, it was
347
+disabled by default, and no one stepped up to defend its existence when
348
+this new editor was created, replacing it. If someone rescues that
349
+feature, merging it in with the new editor, it will doubtless require
350
+JavaScript in order to react to editor button clicks like the “**B**”
351
+button, meaning “make \[selected\] text boldface.” There is no standard
352
+WYSIWYG editor component in browsers, doubtless because it’s relatively
353
+straightforward to create one using JavaScript.
354
+
355
+_Graceful Fallback:_ Unlike in the Fossil 2.11 and earlier days, there
356
+is no longer a script-free wiki editor mode. This is not from lack of
357
+desire, only because the person who wrote the new wiki editor didn’t
358
+want to maintain three different editors. (New Ajaxy editor, old
359
+script-free HTML form based editor, and the old WYSIWYG JavaScript-based
360
+editor.) If someone wants to implement a `<noscript>` alternative to the
361
+new wiki editor, we will likely accept that [contribution][cg] as long
362
+as it doesn’t interfere with the new editor. (The same goes for adding
363
+a WYSIWYG mode to the new Ajaxy wiki editor.)
364
+
365
+_Workaround:_ You don’t have to use the browser-based wiki editor to
366
+maintain your repository’s wiki at all. Fossil’s [`wiki` command][fwc]
367
+lets you manipulate wiki documents from the command line. For example,
368
+consider this Vi based workflow:
369
+
370
+```shell
371
+ $ vi 'My Article.wiki' # begin work on new article
372
+ ...write, write, write...
373
+ :w # save changes to disk copy
374
+ :!fossil wiki create 'My Article' '%' # current file (%) to new article
375
+ ...write, write, write some more...
376
+ :w # save again
377
+ :!fossil wiki commit 'My Article' '%' # update article from disk
378
+ :q # done writing for today
379
+
380
+ ....days later...
381
+ $ vi # work sans named file today
382
+ :r !fossil wiki export 'My Article' - # pull article text into vi buffer
383
+ ...write, write, write yet more...
384
+ :w !fossil wiki commit - # vi buffer updates article
385
+```
386
+
387
+Extending this concept to other text editors is an exercise left to the
388
+reader.
389
+
390
+[fwc]: /help?cmd=wiki
391
+[fwt]: ./wikitheory.wiki
392
+
393
+
394
+### <a id="fedit"></a>The File Editor
395
+
396
+Fossil 2.12 adds the [optional file editor feature][fedit], which works
397
+much like [the new wiki editor](#wedit), only on files committed to the
398
+repository.
399
+
400
+The original designed purpose for this feature is to allow [embedded
401
+documentation][edoc] to be interactively edited in the same way that
402
+wiki articles can be. (Indeed, the associated `fileedit-glob` feature
403
+allows you to restrict the editor to working *only* on files that can be
404
+treated as embedded documentation.) This feature operates in much the
405
+same way as the new wiki editor, so most of what we said above applies.
406
+
407
+_Workaround:_ This feature is an alternative to Fossil’s traditional
408
+mode of file management: clone the repository, open it somewhere, edit a
409
+file locally, and commit the changes.
410
+
411
+_Graceful Fallback:_ There is no technical reason why someone could not
412
+write a `<noscript>` wrapped alternative to the current JavaScript based
413
+`/fileedit` implementation. It would have all of the same downsides as
414
+the old wiki editor: the users would lose their place on each save, they
415
+would have no local backup if something crashes, etc. Still, we are
416
+likely to accept such a [contribution][cg] as long as it doesn’t
417
+interfere with the new editor.
418
+
419
+[edoc]: /doc/trunk/www/embeddeddoc.wiki
420
+[fedit]: /doc/trunk/www/fileedit-page.md
168421
169422
170423
### <a id="ln"></a>Line Numbering
171424
172425
When viewing source files, Fossil offers to show line numbers in some
173
-cases. Toggling them on and off is currently handled in JavaScript.
174
-([Example][mainc].)
426
+cases. ([Example][mainc].) Toggling them on and off is currently handled
427
+in JavaScript, rather than forcing a page-reload via a button click.
428
+
429
+_Workaround:_ Manually edit the URL to give the “`ln`” query parameter
430
+per [the `/file` docs](/help?cmd=/file).
431
+
432
+_Potential Better Workaround:_ Someone sufficiently interested could
433
+[provide a patch][cg] to add a `<noscript>` wrapped HTML button that
434
+would reload the page with this parameter included/excluded to implement
435
+the toggle via a server round-trip.
436
+
437
+As of Fossil 2.12, there is also a JavaScript-based interactive method
438
+for selecting a range of lines by clicking the line numbers when they’re
439
+visible, then copying the resulting URL to share your selection with
440
+others.
175441
176
-_Workaround:_ Edit the URL to give the “`ln`” query parameter per [the
177
-`/file` docs](/help?cmd=/file), or provide a patch to reload the page
178
-with this parameter included/excluded to implement the toggle via a
179
-server round-trip.
442
+_Workaround:_ These interactive features would be difficult and
443
+expensive (in terms of network I/O) to implement without JavaScript. A
444
+far simpler alternative is to manually edit the URL, per above.
180445
181446
[mainc]: https://fossil-scm.org/fossil/artifact?ln&name=87d67e745
182447
183448
184449
### <a id="sxsdiff"></a>Side-by-Side Diff Mode
@@ -224,12 +489,12 @@
224489
similar, hovering over that check-in shows a tooltip with details about
225490
the type of artifact the hash refers to and allows you to click to copy
226491
the hash to the clipboard.
227492
228493
_Graceful Fallback:_ When JavaScript is disabled, these tooltips simply
229
-don’t appear. You can then select and copy the hash using your browser,
230
-make “`fossil info`” queries on those hashes, etc.
494
+don’t appear, but you can still select and copy the hash using your
495
+platform’s “copy selected text” feature.
231496
232497
233498
### <a id="bots"></a>Anti-Bot Defenses
234499
235500
Fossil has [anti-bot defenses][abd], and it has some JavaScript code
@@ -254,26 +519,75 @@
254519
255520
_Graceful Fallback:_ Clicking the hamburger menu button with JavaScript
256521
disabled will take you to the `/sitemap` page instead of showing a
257522
simplified version of that page’s content in a drop-down.
258523
259
-_Workaround:_ You can remove this button by [editing the skin][cs]
524
+_Workaround:_ You can remove this button by [editing the skin][cskin]
260525
header.
261526
262527
263528
### <a id="clock"></a>Clock
264529
265530
Some stock Fossil skins include JavaScript-based features such as the
266531
current time of day. The Xekri skin includes this in its header, for
267
-example. A clock feature requires JavaScript not only to get the time
268
-and update inline on the page once a minute, but also so it displays *in
269
-the local time zone.*
270
-
271
-Since none of this code provides a necessary Fossil feature, the core
272
-developers are unlikely to try to make these features work better in the
273
-absence of JavaScript.
274
-
275
-However, we are willing to study patches to make this better. For
276
-example, the wall clock displays could include the page load time in the
277
-dynamically generated HTML shipped from the remote Fossil server, so
278
-that in the absence of JavaScript, you at least get the page generation
279
-time, expressed in the server’s time zone.
532
+example. A clock feature requires JavaScript to get the time on initial
533
+page load and then to update it once a minute.
534
+
535
+You may observe that the server could provide the current time when
536
+generating the page, but the client and server may not be in the same
537
+time zone, and there is no reliably-provided information from the client
538
+that would let the server give the page load time in the client’s local
539
+time zone. The server could only tell you *its* local time at page
540
+request time, not the client’s time. That still wouldn’t be a “clock,”
541
+since without client-side JavaScript code running, that part of the page
542
+couldn’t update once a second.
543
+
544
+_Potential Graceful Fallback:_ You may consider showing the server’s
545
+page generation time rather than the client’s wall clock time in the
546
+local time zone to be a useful fallback for the current feature, so [a
547
+patch to do this][cg] may well be accepted. Since this is not a
548
+*necessary* Fossil feature, an interested user is unlikely to get the
549
+core developers to do this work for them.
550
+
551
+----
552
+
553
+## <a id="future"></a>Future Plans for JavaScript in Fossil
554
+
555
+As of mid-2020, the informal provisional plan is to increase Fossil
556
+UI's use of JavaScript considerably compared to its historically minimal
557
+uses. To that end, a framework of Fossil-centric APIs is being developed
558
+in conjunction with new features to consolidate Fossil's historical
559
+hodgepodge of JavaScript snippets into a coherent code base.
560
+
561
+When deciding which features to port to JavaScript, the rules of thumb
562
+for this ongoing effort are:
563
+
564
+- Pages which primarily display data (e.g. the timeline) will remain
565
+ largely static HTML with graceful fallbacks for all places they do
566
+ use JavaScript. Though JavaScript can be used effectively to power
567
+ all sorts of wonderful data presentation, Fossil currently doesn't
568
+ benefit greatly from doing so. We use JavaScript on these pages only
569
+ to improve their usability, not to define their primary operations.
570
+
571
+- Pages which act as editors of some sort (e.g. the `/info` page) are
572
+ prime candidates for getting the same treatment as the old wiki
573
+ editor: reimplemented from the ground up in JavaScript using Ajax
574
+ type techniques. Similarly, a JS-driven overhaul is planned for the
575
+ forum’s post editor.
576
+
577
+These are guidelines, not immutable requirements. Our development
578
+direction is guided by our priorities:
579
+
580
+1) Features the developers themselves want to have and/or work on.
581
+
582
+2) Features end users request which catch the interest of one or more
583
+developers, provided the developer(s) in question are in a position to
584
+expend the effort.
585
+
586
+3) Features end users and co-contributors can convince a developer into
587
+coding even when they really don't want to. 😉
588
+
589
+In all of this, Fossil's project lead understandably has the final
590
+say-so in whether any given feature indeed gets merged into the mainline
591
+trunk. Development of any given feature, no matter how much effort was
592
+involved, does not guarantee its eventual inclusion into the public
593
+releases.
280594
--- www/javascript.md
+++ www/javascript.md
@@ -1,28 +1,28 @@
1 # Use of JavaScript in Fossil
2
3 ## Philosophy
4
5 The Fossil development project’s policy is to use JavaScript where it
6 helps make its web UI better, but to offer graceful fallbacks wherever
7 practical. The intent is that the UI be usable with JavaScript entirely
8 disabled. In every place where Fossil uses JavaScript, it is an
9 enhancement to provided functionality, and there is always another way
10 to accomplish a given end without using JavaScript.
11
12 This is not to say that Fossil’s fall-backs for such cases are always as
13 elegant and functional as a no-JS purist might wish. That is simply
14 because [the vast majority of web users run with JS enabled](#stats),
15 and a minority of those run with some kind of conditional JavaScript
16 blocking in place. Fossil’s active developers do not deviate from that
17 norm enough that we have many no-JS purists among us, so the no-JS case
18 doesn’t get as much attention as some might want. We do [accept code
19 contributions][cg], and we are philosophically in favor of graceful
20 fall-backs, so you are welcome to appoint yourself the position of no-JS
21 czar for the Fossil project!
22
23 Evil is in actions, not in nouns, so we do not believe JavaScript *can*
24 be evil. It is an active technology, but the actions that matter here
25 are those of writing the code and checking it into the Fossil project
26 repository. None of the JavaScript code in Fossil is evil, a fact we
27 enforce by being careful about who we give check-in rights on the
28 repository to and by policing what code does get contributed. The Fossil
@@ -30,98 +30,264 @@
30
31 We think it’s better to ask not whether Fossil requires JavaScript but
32 whether Fossil uses JavaScript *well*, so that [you can decide](#block)
33 to block or allow Fossil’s use of JavaScript.
34
 
 
 
 
 
 
 
 
 
 
 
35 [cg]: ./contribute.wiki
36
37
38 ## <a id="block"></a>Blocking JavaScript
39
40 Rather than either block JavaScript wholesale or give up on blocking
41 JavaScript entirely, we recommend that you use tools like [NoScript][ns]
42 or [uBlock Origin][ub] to selectively block problematic uses of
43 JavaScript so the rest of the web can use the technology productively,
44 as it was intended. There are doubtless other useful tools of this sort;
45 we recommend only these two due to our limited experience, not out of
46 any wish to exclude other tools.
47
48 The primary difference between these two for our purposes is that
49 NoScript lets you select scripts to run on a page on a case-by-case
50 basis, whereas uBlock Origin delegates those choices to a group of
51 motivated volunteers who maintain whitelists and blacklists to control
52 all of this; you can then override UBO’s stock rules as needed.
53
54 [ns]: https://noscript.net/
55 [ub]: https://github.com/gorhill/uBlock/
56
57
58 ## <a id="stats"></a>How Many Users Run with JavaScript Disabled Anyway?
59
60 There are several studies that have directly measured the web audience
61 to answer this question:
62
63 * [What percentage of browsers with javascript disabled?][s1]
64 * [How many people are missing out on JavaScript enhancement?][s2]
65 * [Just how many web users really disable cookies or JavaScript?][s3]
66
67 Our sense of this data is that only about 0.2% of web users had
68 JavaScript disabled while participating in these studies.
69
70 The Fossil user community is not typical of the wider web, but if we
71 were able to comprehensively survey our users, we’d expect to find an
72 interesting dichotomy. Because Fossil is targeted at software
73 developers, who in turn are more likely to be power-users, we’d expect
74 to find Fossil users to be more in favor of some amount of JavaScript
75 blocking than the average web user. Yet, we’d also expect to find that
76 our user base has a disproportionately high number who run [powerful
77 conditional blocking plugins](#block) in their browsers, rather than
78 block JS entirely. We suspect that between these two forces, the number
79 of no-JS purists among Fossil’s user base is still a tiny minority.
80
81 [s1]: https://blockmetry.com/blog/javascript-disabled
82 [s2]: https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/
83 [s3]: https://w3techs.com/technologies/overview/client_side_language/all
84
85
86 ## <a id="3pjs"></a>No Third-Party JavaScript in Fossil
87
88 Fossil does not use any third-party JavaScript libraries, not even very
89 common ones like jQuery. Every bit of JavaScript served by the stock
90 version of Fossil was written specifically for the Fossil project and is
91 stored [in its code repository](https://fossil-scm.org/fossil/file).
92
93 Therefore, if you want to hack on the JavaScript code served by Fossil
94 and mechanisms like [skin editing][cs] don’t suffice for your purposes,
95 you can hack on the JavaScript in your local instance directly, just as
96 you can hack on its C, SQL, and Tcl code. Fossil is free and open source
97 software, under [a single license][2cbsd].
98
99 [2cbsd]: https://fossil-scm.org/home/doc/trunk/COPYRIGHT-BSD2.txt
100 [cs]: ./customskin.md
101
102
103 ## <a id="snoop"></a>Fossil Does Not Snoop On You
104
105 There is no tracking or other snooping technology in Fossil other than
106 that necessary for basic security, such as IP address logging on
107 check-ins. (This is in part why we have no [comprehensive user
108 statistics](#stats)!)
109
110 Fossil attempts to set two cookies on all web clients: a login session
111 cookie and a display preferences cookie. These cookies are restricted to
112 the Fossil instance, so even this limited data cannot leak between
113 Fossil instances or into other web sites.
114
115 There is some server-side event logging, but that is done entirely
116 without JavaScript, so it’s off-topic here.
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
119 ## <a id="uses"></a>Places Where Fossil’s Web UI Uses JavaScript
120
121 The remainder of this document will explain how Fossil currently uses
122 JavaScript and what it does when these uses are blocked.
 
123
124
125 ### <a id="timeline"></a>Timeline Graph
126
127 Fossil’s [web timeline][wt] uses JavaScript to render the graph
@@ -140,45 +306,144 @@
140 command, by clicking around within the web UI, etc.
141
142 _Potential Workaround:_ The timeline could be enhanced with `<noscript>`
143 tags that replace the graph with a column of checkboxes that control
144 what a series of form submit buttons do when clicked, replicating the
145 current JS-based features of the graph using client-server round-trips.
146 For example, you could click two of those checkboxes and then a button
147 labeled “Diff Selected” to replicate the current “click two nodes to
148 diff them” feature.
149
150 [wt]: https://fossil-scm.org/fossil/timeline
151
152
153 ### <a id="wedit"></a>WYSIWYG Wiki Editor
154
155 The Admin → Wiki → “Enable WYSIWYG Wiki Editing” toggle switches the
156 default plaintext editor for [Fossil wiki][fw] documents to one that
157 works like a basic word processor. This feature requires JavaScript in
158 order to react to editor button clicks like the “**B**” button, meaning
159 “make \[selected\] text boldface.” There is no standard WYSIWYG editor
160 component in browsers, doubtless because it’s relatively straightforward
161 to create one using JavaScript.
162
163 _Graceful Fallback:_ Edit your wiki documents in the default plain text
164 wiki editor. Fossil’s wiki and Markdown language processors were
165 designed to be edited that way.
166
167 [fw]: ./wikitheory.wiki
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
169
170 ### <a id="ln"></a>Line Numbering
171
172 When viewing source files, Fossil offers to show line numbers in some
173 cases. Toggling them on and off is currently handled in JavaScript.
174 ([Example][mainc].)
 
 
 
 
 
 
 
 
 
 
 
 
 
175
176 _Workaround:_ Edit the URL to give the “`ln`” query parameter per [the
177 `/file` docs](/help?cmd=/file), or provide a patch to reload the page
178 with this parameter included/excluded to implement the toggle via a
179 server round-trip.
180
181 [mainc]: https://fossil-scm.org/fossil/artifact?ln&name=87d67e745
182
183
184 ### <a id="sxsdiff"></a>Side-by-Side Diff Mode
@@ -224,12 +489,12 @@
224 similar, hovering over that check-in shows a tooltip with details about
225 the type of artifact the hash refers to and allows you to click to copy
226 the hash to the clipboard.
227
228 _Graceful Fallback:_ When JavaScript is disabled, these tooltips simply
229 don’t appear. You can then select and copy the hash using your browser,
230 make “`fossil info`” queries on those hashes, etc.
231
232
233 ### <a id="bots"></a>Anti-Bot Defenses
234
235 Fossil has [anti-bot defenses][abd], and it has some JavaScript code
@@ -254,26 +519,75 @@
254
255 _Graceful Fallback:_ Clicking the hamburger menu button with JavaScript
256 disabled will take you to the `/sitemap` page instead of showing a
257 simplified version of that page’s content in a drop-down.
258
259 _Workaround:_ You can remove this button by [editing the skin][cs]
260 header.
261
262
263 ### <a id="clock"></a>Clock
264
265 Some stock Fossil skins include JavaScript-based features such as the
266 current time of day. The Xekri skin includes this in its header, for
267 example. A clock feature requires JavaScript not only to get the time
268 and update inline on the page once a minute, but also so it displays *in
269 the local time zone.*
270
271 Since none of this code provides a necessary Fossil feature, the core
272 developers are unlikely to try to make these features work better in the
273 absence of JavaScript.
274
275 However, we are willing to study patches to make this better. For
276 example, the wall clock displays could include the page load time in the
277 dynamically generated HTML shipped from the remote Fossil server, so
278 that in the absence of JavaScript, you at least get the page generation
279 time, expressed in the server’s time zone.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
--- www/javascript.md
+++ www/javascript.md
@@ -1,28 +1,28 @@
1 # Use of JavaScript in Fossil
2
3 ## Philosophy & Policy
4
5 The Fossil development project’s policy is to use JavaScript where it
6 helps make its web UI better, but to offer graceful fallbacks wherever
7 practical. The intent is that the UI be usable with JavaScript
8 entirely disabled. In almost all places where Fossil uses JavaScript,
9 it is an enhancement to provided functionality, and there is always
10 another way to accomplish a given end without using JavaScript.
11
12 This is not to say that Fossil’s fall-backs for such cases are always as
13 elegant and functional as a no-JS purist might wish. That is simply
14 because [the vast majority of web users run with JavaScript enabled](#stats),
15 and a minority of those run with some kind of [conditional JavaScript
16 blocking](#block) in place. Fossil’s active developers do not deviate from that
17 norm enough that we have many no-JS purists among us, so the no-JS case
18 doesn’t get as much attention as some might want. We do [accept code
19 contributions][cg], and we are philosophically in favor of graceful
20 fall-backs, so you are welcome to appoint yourself the position of no-JS
21 czar for the Fossil project!
22
23 Evil is in actions, not in nouns: we do not believe JavaScript *can*
24 be evil. It is an active technology, but the actions that matter here
25 are those of writing the code and checking it into the Fossil project
26 repository. None of the JavaScript code in Fossil is evil, a fact we
27 enforce by being careful about who we give check-in rights on the
28 repository to and by policing what code does get contributed. The Fossil
@@ -30,98 +30,264 @@
30
31 We think it’s better to ask not whether Fossil requires JavaScript but
32 whether Fossil uses JavaScript *well*, so that [you can decide](#block)
33 to block or allow Fossil’s use of JavaScript.
34
35 The Fossil developers want to see the project thrive, and we achieve
36 that best by making it usable and friendly to a wider audience than the
37 minority of static web app purists. Modern users generally expect a
38 smoother experience than was available with 1990s style HTTP
39 POST-and-response `<form>` based interaction. We also increase the set
40 of potential Fossil developers if we do not restrict them to such
41 antiquated methods.
42
43 JavaScript is not perfect, but it's what we have, so we will use it
44 where we find it advantageous.
45
46 [cg]: ./contribute.wiki
47
48
49 ## <a id="debate"></a>Arguments Against JavaScript & Our Rebuttals
50
51 There are many common arguments against the use of JavaScript. Rather than
52 rehash these same arguments on the [forum][ffor], we distill the common
53 ones we’ve heard before and give our stock answers to them here:
54
55 1. “**It increases the size of the page download.**”
56
57 The heaviest such pages served by Fossil only have about 8 kB of
58 compressed JavaScript. (You have to go out of your way to get Fossil
59 to serve uncompressed pages.) This is negligible, even over very
60 slow data connections. If you are still somehow on a 56 kbit/sec
61 analog telephone modem, this extra script code would download in
62 about a second.
63
64 Most JavaScript-based Fossil pages use less code than that.
65
66 Atop that, Fossil 2.12 adds new script delivery methods with
67 aggressive caching enabled so that typical page loads will skip
68 re-loading this content on subsequent loads. These features are
69 currently optional: you must either set the new [`fossil server
70 --jsmode` option][fsrv] or the corresponding `jsmode` control line
71 in your [`fossil cgi`][fcgi] script when setting up your
72 [Fossil server][fshome]. That done, Fossil’s JavaScript files will
73 load almost instantly from the browser’s cache after the initial
74 page load, rather than be re-transferred over the network.
75
76 Between the improved caching and the fact that it’s quicker to
77 transfer a partial Ajax page load than reload the entire page, the
78 aggregate cost of such pages is typically *lower* than the older
79 methods based on HTTP POST with a full server round-trip. You can
80 expect to recover the cost of the initial page load in 1-2
81 round-trips. If we were to double the amount of JavaScript code in
82 Fossil, the payoff time would increase to 2-4 round-trips.
83
84 2. “**JavaScript is slow.**”
85
86 It *was*, before September 2008. Google's introduction of [their V8
87 JavaScript engine][v8] taught the world that JavaScript need not be
88 slow. This competitive pressure caused the other common JavaScript
89 interpreters to either improve or be replaced by one of the engines
90 that did improve to approach V8’s speed.
91
92 Nowadays JavaScript is, as a rule, astoundingly fast. As the world
93 continues to move more and more to web-based applications and
94 services, JavaScript engine developers have ample motivation to keep
95 their engines fast and competitive.
96
97 Once the scripts are cached, Ajax based page updates are faster than
98 the alternative, a full HTTP POST round-trip.
99
100 3. <a id="3pjs"></a>“**Third-party JavaScript cannot be trusted.**”
101
102 Fossil does not use any third-party JavaScript libraries, not even
103 very common ones like jQuery. Every bit of JavaScript served by the
104 stock version of Fossil was written specifically for the Fossil
105 project and is stored [in its code repository][fsrc].
106
107 Therefore, if you want to hack on the JavaScript code served by
108 Fossil and mechanisms like [skin editing][cskin] don’t suffice for your
109 purposes, you can hack on the JavaScript in your local instance
110 directly, just as you can hack on its C, SQL, and Tcl code. Fossil
111 is free and open source software, under [a single license][2cbsd].
112
113 4. <a id="snoop"></a>“**JavaScript and cookies are used to snoop on web users.**”
114
115 There is no tracking or other snooping technology in Fossil other than
116 that necessary for basic security, such as IP address logging on
117 check-ins. (This is in part why we have no [comprehensive user
118 statistics](#stats)!)
119
120 Fossil attempts to set two cookies on all web clients: a login session
121 cookie and a display preferences cookie. These cookies are restricted to
122 the Fossil instance, so even this limited data cannot leak between
123 Fossil instances or into other web sites.
124
125 5. “**JavaScript is fundamentally insecure.**”
126
127 JavaScript is certainly sometimes used for nefarious ends, but if we
128 wish to have more features in Fossil, the alternative is to add more
129 code to the Fossil binary, [most likely in C][fslpl], a language
130 implicated in [over 4× more security vulnerabilities][whmsl].
131
132 Therefore, does it not make sense to place approximately four times
133 as much trust in Fossil’s JavaScript code as in its C code?
134
135 The question is not whether JavaScript is itself evil, it is whether
136 its *authors* are evil. *Every byte* of JavaScript code used within
137 the Fossil UI is:
138
139 * ...written by the Fossil developers, vetted by their peers.
140
141 * ...[open source][flic] and [available][fsrc] to be inspected,
142 audited, and changed by its users.
143
144 * ...compiled directly into the `fossil` binary in a
145 non-obfuscated form during the build process, so there are no
146 third-party servers delivering mysterious, obfuscated JavaScript
147 code blobs to the user.
148
149 Local administrators can [modify the repository’s skin][cskin] to
150 inject additional JavaScript code into pages served by their Fossil
151 server. A typical case is to add a syntax highlighter like
152 [Prism.js][pjs] or [highlightjs][hljs] to the local repository. At
153 that point, your trust concern is not with Fossil’s use of
154 JavaScript, but with your trust in that repository’s administrator.
155
156 Fossil's [default content security policy][dcsp] (CSP)
157 prohibits execution of JavaScript code which is delivered from
158 anywhere but the Fossil server which delivers the page. A local
159 administrator can change this CSP, but again this comes down to a
160 matter of trust with the administrator, not with Fossil itself.
161
162 6. “**Cross-browser compatibility is poor.**”
163
164 It most certainly was in the first decade or so of JavaScript’s
165 lifetime, resulting in the creation of powerful libraries like
166 jQuery to patch over the incompatibilities. Over time, the need for
167 such libraries has dropped as browser vendors have fixed the
168 incompatibilities. Cross-browser JavaScript compatibility issues
169 which affect web developers are, by and large, a thing of the past.
170
171 7. “**Fossil UI works fine today without JavaScript. Why break it?**”
172
173 While this is true today, and we have no philosophical objection to
174 it remaining true, we do not intend to limit ourselves to only those
175 features that can be created without JavaScript. The mere
176 availability of alternatives is not a good justification for holding
177 back on notable improvements when they're within easy reach.
178
179 The no-JS case is a [minority position](#stats), so those that want
180 Fossil to have no-JS alternatives and graceful fallbacks will need
181 to get involved with the development if they want this state of
182 affairs to continue.
183
184 8. <a id="stats"></a>“**A large number of users run without JavaScript enabled.**”
185
186 That’s not what web audience measurements say:
187
188 * [What percentage of browsers with javascript disabled?][s1]
189 * [How many people are missing out on JavaScript enhancement?][s2]
190 * [Just how many web users really disable cookies or JavaScript?][s3]
191
192 Our sense of this data is that only about 0.2% of web users had
193 JavaScript disabled while participating in these studies.
194
195 The Fossil user community is not typical of the wider web, but if we
196 were able to comprehensively survey our users, we’d expect to find
197 an interesting dichotomy. Because Fossil is targeted at software
198 developers, who in turn are more likely to be power-users, we’d
199 expect to find Fossil users to be more in favor of some amount of
200 JavaScript blocking than the average web user. Yet, we’d also expect
201 to find that our user base has a disproportionately high number who
202 run [powerful conditional blocking plugins](#block) in their
203 browsers, rather than block JavaScript entirely. We suspect that
204 between these two forces, the number of no-JS purists among Fossil’s
205 user base is still a tiny minority.
206
207 9. <a id="block"></a>“**I block JavaScript entirely in my browser. That breaks Fossil.**”
208
209 First, see our philosophy statements above. Briefly, we intend that
210 there always be some other way to get any given result without using
211 JavaScript, developer interest willing.
212
213 But second, it doesn’t have to be all-or-nothing. We recommend that
214 those interested in blocking problematic uses of JavaScript use
215 tools like [NoScript][ns] or [uBlock Origin][ubo] to *selectively*
216 block JavaScript so the rest of the web can use the technology
217 productively, as it was intended.
218
219 There are doubtless other useful tools of this sort. We recommend
220 these two only from our limited experience, not out of any wish to
221 exclude other tools.
222
223 The primary difference between these two for our purposes is that
224 NoScript lets you select scripts to run on a page on a case-by-case
225 basis, whereas uBlock Origin delegates those choices to a group of
226 motivated volunteers who maintain allow/block lists to control all
227 of this; you can then override UBO’s stock rules as needed.
228
229 10. “**My browser doesn’t even *have* a JavaScript interpreter.**”
230
231 The Fossil open source project has no full-time developers, and only
232 a few of these part-timers are responsible for the bulk of the code
233 in Fossil. If you want Fossil to support such niche use cases, then
234 you will have to [get involved with its development][cg]: it’s
235 *your* uncommon itch.
236
237 11. <a id="compat"></a>“**Fossil’s JavaScript code isn’t compatible with my browser.**”
238
239 The Fossil project’s developers aim to remain compatible with
240 the largest portions of the client-side browser base. We use only
241 standards-defined JavaScript features which are known to work in the
242 overwhelmingly vast majority of browsers going back approximately 5
243 years, at minimum, as documented by [Can I Use...?][ciu] We avoid use of
244 features added to the language more recently or those which are still in
245 flux in standards committees.
246
247 We set this threshold based on the amount of time it typically takes for
248 new standards to propagate through the installed base.
249
250 As of this writing, this means we are only using features defined in
251 [ECMAScript 2015][es2015], colloquially called “JavaScript 6.” That
252 is a sufficiently rich standard that it more than suffices for our
253 purposes, and it is [widely deployed][es6dep]. The biggest single
254 outlier remaining is MSIE 11, and [even Microsoft is moving their
255 own products off of it][ie11x].
256
257 [2cbsd]: https://fossil-scm.org/home/doc/trunk/COPYRIGHT-BSD2.txt
258 [ciu]: https://caniuse.com/
259 [cskin]: ./customskin.md
260 [dcsp]: ./defcsp.md
261 [es2015]: https://ecma-international.org/ecma-262/6.0/
262 [es6dep]: https://caniuse.com/#feat=es6
263 [fcgi]: /help?cmd=cgi
264 [ffor]: https://fossil-scm.org/forum/
265 [flic]: /doc/trunk/COPYRIGHT-BSD2.txt
266 [fshome]: /doc/trunk/www/server/
267 [fslpl]: /doc/trunk/www/fossil-v-git.wiki#portable
268 [fsrc]: https://fossil-scm.org/home/file/src
269 [fsrv]: /help?cmd=server
270 [hljs]: https://fossil-scm.org/forum/forumpost/9150bc22ca
271 [ie11x]: https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666
272 [ns]: https://noscript.net/
273 [pjs]: https://fossil-scm.org/forum/forumpost/1198651c6d
274 [s1]: https://blockmetry.com/blog/javascript-disabled
275 [s2]: https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/
276 [s3]: https://w3techs.com/technologies/overview/client_side_language/all
277 [ubo]: https://github.com/gorhill/uBlock/
278 [v8]: https://en.wikipedia.org/wiki/V8_(JavaScript_engine)
279 [whmsl]: https://www.whitesourcesoftware.com/most-secure-programming-languages/
280
281
282 ----
283
284 ## <a id="uses"></a>Places Where Fossil’s Web UI Uses JavaScript
285
286 This section documents the areas where Fossil currently uses JavaScript
287 and what it does when these uses are blocked. It also gives common
288 workarounds where necessary.
289
290
291 ### <a id="timeline"></a>Timeline Graph
292
293 Fossil’s [web timeline][wt] uses JavaScript to render the graph
@@ -140,45 +306,144 @@
306 command, by clicking around within the web UI, etc.
307
308 _Potential Workaround:_ The timeline could be enhanced with `<noscript>`
309 tags that replace the graph with a column of checkboxes that control
310 what a series of form submit buttons do when clicked, replicating the
311 current JavaScript-based features of the graph using client-server round-trips.
312 For example, you could click two of those checkboxes and then a button
313 labeled “Diff Selected” to replicate the current “click two nodes to
314 diff them” feature.
315
316 [wt]: https://fossil-scm.org/fossil/timeline
317
318
319 ### <a id="wedit"></a>The New Wiki Editor
320
321 The [new wiki editor][fwt] added in Fossil 2.12 has many new features, a
322 few of which are impossible to get without use of JavaScript.
323
324 First, it allows in-browser previews without losing client-side editor
325 state, such as where your cursor is. With the old editor, you had to
326 re-locate the place you were last editing on each preview, which would
327 reduce the incentive to use the preview function. In the new wiki
328 editor, you just click the Preview tab to see how Fossil interprets your
329 markup, then click back to the Editor tab to resume work with the prior
330 context undisturbed.
331
332 Second, it continually saves your document state in client-side storage
333 in the background while you’re editing it so that if the browser closes
334 without saving the changes back to the Fossil repository, you can resume
335 editing from the stored copy without losing work. This feature is not so
336 much about saving you from crashes of various sorts, since computers are
337 so much more reliable these days. It is far more likely to save you from
338 the features of mobile OSes like Android and iOS which aggressively shut
339 down and restart apps to save on RAM. That OS design philosophy assumes
340 that there is a way for the app to restore its prior state from
341 persistent media when it’s restarted, giving the illusion that it was
342 never shut down in the first place. This feature of Fossil’s new wiki
343 editor provides that.
344
345 With this change, we lost the old WYSIWYG wiki editor, available since
346 Fossil version 1.24. It hadn’t been maintained for years, it was
347 disabled by default, and no one stepped up to defend its existence when
348 this new editor was created, replacing it. If someone rescues that
349 feature, merging it in with the new editor, it will doubtless require
350 JavaScript in order to react to editor button clicks like the “**B**”
351 button, meaning “make \[selected\] text boldface.” There is no standard
352 WYSIWYG editor component in browsers, doubtless because it’s relatively
353 straightforward to create one using JavaScript.
354
355 _Graceful Fallback:_ Unlike in the Fossil 2.11 and earlier days, there
356 is no longer a script-free wiki editor mode. This is not from lack of
357 desire, only because the person who wrote the new wiki editor didn’t
358 want to maintain three different editors. (New Ajaxy editor, old
359 script-free HTML form based editor, and the old WYSIWYG JavaScript-based
360 editor.) If someone wants to implement a `<noscript>` alternative to the
361 new wiki editor, we will likely accept that [contribution][cg] as long
362 as it doesn’t interfere with the new editor. (The same goes for adding
363 a WYSIWYG mode to the new Ajaxy wiki editor.)
364
365 _Workaround:_ You don’t have to use the browser-based wiki editor to
366 maintain your repository’s wiki at all. Fossil’s [`wiki` command][fwc]
367 lets you manipulate wiki documents from the command line. For example,
368 consider this Vi based workflow:
369
370 ```shell
371 $ vi 'My Article.wiki' # begin work on new article
372 ...write, write, write...
373 :w # save changes to disk copy
374 :!fossil wiki create 'My Article' '%' # current file (%) to new article
375 ...write, write, write some more...
376 :w # save again
377 :!fossil wiki commit 'My Article' '%' # update article from disk
378 :q # done writing for today
379
380 ....days later...
381 $ vi # work sans named file today
382 :r !fossil wiki export 'My Article' - # pull article text into vi buffer
383 ...write, write, write yet more...
384 :w !fossil wiki commit - # vi buffer updates article
385 ```
386
387 Extending this concept to other text editors is an exercise left to the
388 reader.
389
390 [fwc]: /help?cmd=wiki
391 [fwt]: ./wikitheory.wiki
392
393
394 ### <a id="fedit"></a>The File Editor
395
396 Fossil 2.12 adds the [optional file editor feature][fedit], which works
397 much like [the new wiki editor](#wedit), only on files committed to the
398 repository.
399
400 The original designed purpose for this feature is to allow [embedded
401 documentation][edoc] to be interactively edited in the same way that
402 wiki articles can be. (Indeed, the associated `fileedit-glob` feature
403 allows you to restrict the editor to working *only* on files that can be
404 treated as embedded documentation.) This feature operates in much the
405 same way as the new wiki editor, so most of what we said above applies.
406
407 _Workaround:_ This feature is an alternative to Fossil’s traditional
408 mode of file management: clone the repository, open it somewhere, edit a
409 file locally, and commit the changes.
410
411 _Graceful Fallback:_ There is no technical reason why someone could not
412 write a `<noscript>` wrapped alternative to the current JavaScript based
413 `/fileedit` implementation. It would have all of the same downsides as
414 the old wiki editor: the users would lose their place on each save, they
415 would have no local backup if something crashes, etc. Still, we are
416 likely to accept such a [contribution][cg] as long as it doesn’t
417 interfere with the new editor.
418
419 [edoc]: /doc/trunk/www/embeddeddoc.wiki
420 [fedit]: /doc/trunk/www/fileedit-page.md
421
422
423 ### <a id="ln"></a>Line Numbering
424
425 When viewing source files, Fossil offers to show line numbers in some
426 cases. ([Example][mainc].) Toggling them on and off is currently handled
427 in JavaScript, rather than forcing a page-reload via a button click.
428
429 _Workaround:_ Manually edit the URL to give the “`ln`” query parameter
430 per [the `/file` docs](/help?cmd=/file).
431
432 _Potential Better Workaround:_ Someone sufficiently interested could
433 [provide a patch][cg] to add a `<noscript>` wrapped HTML button that
434 would reload the page with this parameter included/excluded to implement
435 the toggle via a server round-trip.
436
437 As of Fossil 2.12, there is also a JavaScript-based interactive method
438 for selecting a range of lines by clicking the line numbers when they’re
439 visible, then copying the resulting URL to share your selection with
440 others.
441
442 _Workaround:_ These interactive features would be difficult and
443 expensive (in terms of network I/O) to implement without JavaScript. A
444 far simpler alternative is to manually edit the URL, per above.
 
445
446 [mainc]: https://fossil-scm.org/fossil/artifact?ln&name=87d67e745
447
448
449 ### <a id="sxsdiff"></a>Side-by-Side Diff Mode
@@ -224,12 +489,12 @@
489 similar, hovering over that check-in shows a tooltip with details about
490 the type of artifact the hash refers to and allows you to click to copy
491 the hash to the clipboard.
492
493 _Graceful Fallback:_ When JavaScript is disabled, these tooltips simply
494 don’t appear, but you can still select and copy the hash using your
495 platform’s “copy selected text” feature.
496
497
498 ### <a id="bots"></a>Anti-Bot Defenses
499
500 Fossil has [anti-bot defenses][abd], and it has some JavaScript code
@@ -254,26 +519,75 @@
519
520 _Graceful Fallback:_ Clicking the hamburger menu button with JavaScript
521 disabled will take you to the `/sitemap` page instead of showing a
522 simplified version of that page’s content in a drop-down.
523
524 _Workaround:_ You can remove this button by [editing the skin][cskin]
525 header.
526
527
528 ### <a id="clock"></a>Clock
529
530 Some stock Fossil skins include JavaScript-based features such as the
531 current time of day. The Xekri skin includes this in its header, for
532 example. A clock feature requires JavaScript to get the time on initial
533 page load and then to update it once a minute.
534
535 You may observe that the server could provide the current time when
536 generating the page, but the client and server may not be in the same
537 time zone, and there is no reliably-provided information from the client
538 that would let the server give the page load time in the client’s local
539 time zone. The server could only tell you *its* local time at page
540 request time, not the client’s time. That still wouldn’t be a “clock,”
541 since without client-side JavaScript code running, that part of the page
542 couldn’t update once a second.
543
544 _Potential Graceful Fallback:_ You may consider showing the server’s
545 page generation time rather than the client’s wall clock time in the
546 local time zone to be a useful fallback for the current feature, so [a
547 patch to do this][cg] may well be accepted. Since this is not a
548 *necessary* Fossil feature, an interested user is unlikely to get the
549 core developers to do this work for them.
550
551 ----
552
553 ## <a id="future"></a>Future Plans for JavaScript in Fossil
554
555 As of mid-2020, the informal provisional plan is to increase Fossil
556 UI's use of JavaScript considerably compared to its historically minimal
557 uses. To that end, a framework of Fossil-centric APIs is being developed
558 in conjunction with new features to consolidate Fossil's historical
559 hodgepodge of JavaScript snippets into a coherent code base.
560
561 When deciding which features to port to JavaScript, the rules of thumb
562 for this ongoing effort are:
563
564 - Pages which primarily display data (e.g. the timeline) will remain
565 largely static HTML with graceful fallbacks for all places they do
566 use JavaScript. Though JavaScript can be used effectively to power
567 all sorts of wonderful data presentation, Fossil currently doesn't
568 benefit greatly from doing so. We use JavaScript on these pages only
569 to improve their usability, not to define their primary operations.
570
571 - Pages which act as editors of some sort (e.g. the `/info` page) are
572 prime candidates for getting the same treatment as the old wiki
573 editor: reimplemented from the ground up in JavaScript using Ajax
574 type techniques. Similarly, a JS-driven overhaul is planned for the
575 forum’s post editor.
576
577 These are guidelines, not immutable requirements. Our development
578 direction is guided by our priorities:
579
580 1) Features the developers themselves want to have and/or work on.
581
582 2) Features end users request which catch the interest of one or more
583 developers, provided the developer(s) in question are in a position to
584 expend the effort.
585
586 3) Features end users and co-contributors can convince a developer into
587 coding even when they really don't want to. 😉
588
589 In all of this, Fossil's project lead understandably has the final
590 say-so in whether any given feature indeed gets merged into the mainline
591 trunk. Development of any given feature, no matter how much effort was
592 involved, does not guarantee its eventual inclusion into the public
593 releases.
594

Keyboard Shortcuts

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