18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
}, false);
};
document.querySelectorAll('table.diff').forEach(addToggle);
});
window.fossil.onPageLoad(function(){
const F = window.fossil, D = F.dom;
/**
Uses the /jchunk AJAX route to fetch specific lines of a given
artifact. The first argument must be an Object with these
properties:
{
name: full hash of the target file,
from: first 1-based line number of the file to fetch (inclusive),
to: last 1-based line number of the file to fetch (inclusive)
}
onload and onerror are optional callback functions to be called
on success resp. error, as documented for window.fossil.fetch().
Note that onload is ostensibly optional but this function is not
of much use without an onload handler. Conversely, the default
onerror handler is often customized on a per-page basis to send
the error output somewhere where the user can see it.
The response, on success, will be an array of strings, each entry
being one line from the requested artifact. If the 'to' line is
greater than the length of the file, the array will be shorter
than (to-from) lines.
The /jchunk route reports errors via JSON objects with
an "error" string property describing the problem.
This is an async operation. Returns this object.
*/
F.fetchArtifactLines = function(urlParams, onload, onerror){
const opt = {urlParams};
if(onload) opt.onload = onload;
if(onerror) opt.onerror = onerror;
return this.fetch('jchunk', opt);
};
});
/**
2021-09-07: refactoring the following for use in the higher-level
fossil.*.js framework is pending. For now it's a copy/paste copy
of diff.js.
*/
|
>
>
>
>
>
>
>
>
>
>
|
>
>
>
|
>
|
>
>
>
|
>
>
>
>
|
|
|
>
|
|
|
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
|
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
}, false);
};
document.querySelectorAll('table.diff').forEach(addToggle);
});
window.fossil.onPageLoad(function(){
const F = window.fossil, D = F.dom;
const Diff = F.diff = {
config: {
chunkLoadLines: 20,
chunkFetch: {
/* Default callack handlers for Diff.fetchArtifactChunk(),
unless overridden by options passeed to that function. */
beforesend: function(){},
aftersend: function(){},
onerror: function(e){
F.toast.error("XHR error: ",e.message);
}
}
}
};
/**
Uses the /jchunk AJAX route to fetch specific lines of a given
artifact. The argument must be an Object suitable for passing as
the second argument to fossil.fetch(). Its urlParams property
must be an object with these properties:
{
name: full hash of the target file,
from: first 1-based line number of the file to fetch (inclusive),
to: last 1-based line number of the file to fetch (inclusive)
}
The fetchOpt object is NOT cloned for use by the call: it is used
as-is and may be modified by this call. Thus callers "really
should" pass a temporary object, not a long-lived one.
If fetchOpt does not define any of the (beforesend, aftersend,
onerror) callbacks, the defaults from fossil.diff.config.chunkFetch
are used, so any given client page may override those to provide
page-level default handling.
Note that onload callback is ostensibly optional but this
function is not of much use without an onload
handler. Conversely, the default onerror handler is often
customized on a per-page basis to send the error output somewhere
where the user can see it.
The response, on success, will be an array of strings, each entry
being one line from the requested artifact. If the 'to' line is
greater than the length of the file, the array will be shorter
than (to-from) lines.
The /jchunk route reports errors via JSON objects with
an "error" string property describing the problem.
This is an async operation. Returns the fossil object.
*/
Diff.fetchArtifactChunk = function(fetchOpt){
if(!fetchOpt.beforesend) fetchOpt.beforesend = Diff.config.chunkFetch.beforesend;
if(!fetchOpt.aftersend) fetchOpt.aftersend = Diff.config.chunkFetch.aftersend;
if(!fetchOpt.onerror) fetchOpt.onerror = Diff.config.chunkFetch.onerror;
fetchOpt.responseType = 'json';
return F.fetch('jchunk', fetchOpt);
};
/**
Fetches /jchunk for the given TR element then replaces the TR's
contents with data from the result of that request.
*/
const fetchTrChunk = function(tr){
if(tr.dataset.xfer /* already being fetched */) return;
const table = tr.parentElement.parentElement;
const hash = table.dataset.lefthash;
if(!hash) return;
const isSbs = table.classList.contains('splitdiff')/*else udiff*/;
tr.dataset.xfer = 1 /* sentinel against multiple concurrent ajax requests */;
const lineTo = +tr.dataset.endln;
var lnFrom = +tr.dataset.startln;
/* TODO: for the time being, for simplicity, we'll read the whole
[startln, endln] chunk. "Later on" we'll maybe want to read it in
chunks of, say, 20 lines or so, adjusting lnFrom to be 1 if it would
be less than 1. */
Diff.fetchArtifactChunk({
urlParams:{
name: hash,
from: lnFrom,
to: lineTo
},
aftersend: function(){
delete tr.dataset.xfer;
Diff.config.chunkFetch.aftersend.apply(
this, Array.prototype.slice.call(arguments,0)
);
},
onload: function(result){
console.debug("Chunk result: ",result);
D.clearElement(tr);
D.append(
D.attr(D.td(tr), 'colspan', isSbs ? 5 : 4),
"Fetched chunk of ",result.length," line(s). TODO: insert it here."
);
/*
At this point we need to:
- Read the previous TR, if any, to get the preceeding LHS/RHS
line numbers so that we know where to start counting.
- If there is no previous TR, we're at the top and we
instead need to get the LHS/RHS line numbers from the
following TR's children.
- D.clearElement(tr) and insert columns appropriate for the
parent table's diff type.
We can fish the line numbers out of the PRE columns with something
like this inefficient but effective hack:
theElement.innerText.split(/\n+/)
(need /\n+/ instead of '\n' b/c of INS/DEL elements)
Noting that the result array will end with an empty element
due to the trailing \n character, so a call to pop() will be
needed.
SBS diff col layout:
<td><pre>...LHS line numbers...</pre></td>
<td>...code lines...</td>
<td></td> empty for this case.
<td><pre>...RHS line numbers...</pre></td>
<td>...dupe of col 2</td>
Unified diff col layout:
<td>LHS line numbers</td>
<td>RHS line numbers</td>
<td>blank in this case</td>
<td>code line</td>
*/
}
});
};
Diff.addDiffSkipHandlers = function(){
const tables = document.querySelectorAll('table.diff[data-lefthash]');
if(!tables.length) return F;
const addDiffSkipToTr = function f(tr){
D.addClass(tr, 'jchunk');
if(!f._handler){
f._handler = function ff(event){
var e = event.target;
while(e && 'TR' !== e.tagName) e = e.parentElement;
if(!e){
console.error("Internal event-handling error: didn't find TR target.");
return;
}
e.removeEventListener('click',ff);
D.removeClass(e, 'jchunk');
//console.debug("addDiffSkipToTr() Event:",e, event);
fetchTrChunk(e);
};
}
tr.addEventListener('click', f._handler, false);
};
tables.forEach(function(t){
t.querySelectorAll('tr.diffskip[data-startln]').forEach(addDiffSkipToTr);
});
};
F.diff.addDiffSkipHandlers();
});
/**
2021-09-07: refactoring the following for use in the higher-level
fossil.*.js framework is pending. For now it's a copy/paste copy
of diff.js.
*/
|