Fossil

Diff
Login

Differences From Artifact [00aec5b0e8]:

To Artifact [eb4690f24b]:


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
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 first argument must be an Object with these
     properties:
     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.
     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.

     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 this object.
     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);
  };
  F.fetchArtifactLines = function(urlParams, onload, onerror){
    const opt = {urlParams};
    if(onload) opt.onload = onload;
    if(onerror) opt.onerror = onerror;
    return this.fetch('jchunk', opt);
  };

  /**
     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.
*/