Index: src/fossil.diff.js ================================================================== --- src/fossil.diff.js +++ src/fossil.diff.js @@ -117,10 +117,11 @@ example of which, for use as a model, is: https://github.com/msteveb/autosetup/commit/235925e914a52a542 */ const ChunkLoadControls = function(tr){ + this.$fetchQueue = []; this.e = {/*DOM elements*/ tr: tr, table: tr.parentElement/*TBODY*/.parentElement }; this.isSplit = this.e.table.classList.contains('splitdiff')/*else udiff*/; @@ -174,11 +175,11 @@ if(this.pos.prev && this.pos.next && ((this.pos.next.startLhs - this.pos.prev.endLhs) <= Diff.config.chunkLoadLines)){ /* Place a single button to load the whole block, rather than separate up/down buttons. */ - btnDown = false; + btnDown = this.createButton(this.FetchType.FillGap); btnUp = this.createButton(this.FetchType.FillGap); }else{ /* Figure out which chunk-load buttons to add... */ if(this.pos.prev){ btnDown = this.createButton(this.FetchType.PrevDown); @@ -209,11 +210,13 @@ /** Fill a complete gap between the previous/next diff chunks or at the start of the next chunk or end of the previous chunks. */ FillGap: 0, /** Prepend context to the start of the next diff chunk. */ - NextUp: -1 + NextUp: -1, + /** Process the next queued action. */ + ProcessQueue: 0x7fffffff }, config: { /* glyphUp: '⇡', //'&#uarr;', glyphDown: '⇣' //'&#darr;' @@ -262,10 +265,11 @@ }, /* Attempt to clean up resources and remove some circular references to that GC can do the right thing. */ destroy: function(){ + delete this.$fetchQueue; D.remove(this.e.tr); delete this.e.tr.$chunker; delete this.e.tr; delete this.e; delete this.pos; @@ -281,11 +285,16 @@ */ maybeReplaceButtons: function(){ if(this.pos.next && this.pos.prev && (this.pos.endLhs - this.pos.startLhs <= Diff.config.chunkLoadLines)){ D.clearElement(this.e.btnWrapper); - D.append(this.e.btnWrapper, this.createButton(this.FetchType.FillGap)); + for( var i=0; i<2; i++ ){ + D.append(this.e.btnWrapper, this.createButton(this.FetchType.FillGap)); + } + if( this.$fetchQueue && this.$fetchQueue.length>0 ){ + this.$fetchQueue = [this.FetchType.FillGap]; + } } return this; }, /** @@ -501,28 +510,40 @@ This is an async operation. While it is in transit, any calls to this function will have no effect except (possibly) to emit a warning. Returns this object. */ fetchChunk: function(fetchType){ + if( !this.$fetchQueue ) return this; // HACKHACK: are we destroyed? + if( fetchType==this.FetchType.ProcessQueue ){ + if( this.$fetchQueue.length==0 ) return this; + //console.log('fetchChunk: processing queue ...'); + } + else{ + this.$fetchQueue.push(fetchType); + if( this.$fetchQueue.length!=1 ) return this; + //console.log('fetchChunk: processing user input ...'); + } + fetchType = this.$fetchQueue[0]; /* Forewarning, this is a bit confusing: when fetching the previous lines, we're doing so on behalf of the *next* diff chunk (this.pos.next), and vice versa. */ - if(this.$isFetching){ - return this.msg(true,"Cannot load chunk while a load is pending."); - } if(fetchType===this.FetchType.NextUp && !this.pos.next || fetchType===this.FetchType.PrevDown && !this.pos.prev){ console.error("Attempt to fetch diff lines but don't have any."); return this; } - this.msg(false,"Fetching diff chunk..."); + this.msg(false); const fOpt = { urlParams:{ name: this.fileHash, from: 0, to: 0 }, - aftersend: ()=>delete this.$isFetching, - onload: (list)=>this.injectResponse(fetchType,up,list) + onload: function(list){ + this.injectResponse(fetchType,up,list); + if( !this.$fetchQueue || this.$fetchQueue.length==0 ) return; + this.$fetchQueue.shift(); + setTimeout(this.fetchChunk.bind(this,this.FetchType.ProcessQueue)); + }.bind(this) }; const up = fOpt.urlParams; if(fetchType===this.FetchType.FillGap){ /* Easiest case: filling a whole gap. */ up.from = this.pos.startLhs; @@ -551,13 +572,15 @@ if( this.pos.prev && this.pos.prev.endLhs >= up.from ){ up.from = this.pos.prev.endLhs + 1; fetchType = this.FetchType.FillGap; } } - this.$isFetching = true; //console.debug("fetchChunk(",fetchType,")",up); - fOpt.onerror = (err)=>this.msg(true,err.message); + fOpt.onerror = function(err){ + this.msg(true,err.message); + this.$fetchQueue = []; + }.bind(this); Diff.fetchArtifactChunk(fOpt); return this; } };