Fossil SCM
Doc additions. Resolve two corner-case bugs in the JS attachment widget.
Commit
7723db560690b745589dcc545744c16a39964a9e2eca6ca53b3af198894a3dc2
Parent
da17e05356abed7…
1 file changed
+17
-21
+17
-21
| --- src/fossil.attach.js | ||
| +++ src/fossil.attach.js | ||
| @@ -6,23 +6,27 @@ | ||
| 6 | 6 | Requires that window.fossil has already been set up. |
| 7 | 7 | Depends on fossil.dom. |
| 8 | 8 | */ |
| 9 | 9 | (function(namespace){ |
| 10 | 10 | "use strict"; |
| 11 | - const F = namespace, D = fossil.dom; | |
| 11 | + const F = namespace, D = F.dom; | |
| 12 | 12 | |
| 13 | 13 | let idCounter = 0; |
| 14 | 14 | |
| 15 | 15 | /** |
| 16 | 16 | Implements a multi-file selector widget. Intended to be plugged |
| 17 | 17 | in to places in Fossil's UI where attachments can be assigned to |
| 18 | 18 | an artifact. |
| 19 | 19 | */ |
| 20 | 20 | class Attacher { |
| 21 | + /* Options. */ | |
| 21 | 22 | #opt; |
| 23 | + /* List of objects representing each row. */ | |
| 22 | 24 | #rows = []; |
| 25 | + /* DOM elements */ | |
| 23 | 26 | #e = Object.create(null); |
| 27 | + /* Proxy for various events this object fires. */ | |
| 24 | 28 | #events = new EventTarget(); |
| 25 | 29 | |
| 26 | 30 | /** |
| 27 | 31 | Options: |
| 28 | 32 | |
| @@ -147,11 +151,11 @@ | ||
| 147 | 151 | get isDryRun(){ |
| 148 | 152 | return !!this.#opt.dryRun; |
| 149 | 153 | } |
| 150 | 154 | /** |
| 151 | 155 | Returns the DOM element (div.attach-controls) which wraps the |
| 152 | - "Add" button. Clients may add buttons to it. | |
| 156 | + "Add" button. Clients may add buttons to it. | |
| 153 | 157 | */ |
| 154 | 158 | get controlsElement(){ |
| 155 | 159 | return this.#e.controls; |
| 156 | 160 | } |
| 157 | 161 | |
| @@ -182,20 +186,17 @@ | ||
| 182 | 186 | row: rowObj, |
| 183 | 187 | attacher: this |
| 184 | 188 | }) |
| 185 | 189 | }) |
| 186 | 190 | ); |
| 187 | - if( false /* arguable */ | |
| 188 | - && 0===this.#rows.length | |
| 189 | - && this.#opt.startWith>0 ){ | |
| 190 | - /* Intended primarily for /addattach. */ | |
| 191 | - this.#addRow(); | |
| 192 | - } | |
| 193 | 191 | } |
| 194 | 192 | |
| 193 | + /** | |
| 194 | + Removes all attachments and clears the error state. | |
| 195 | + */ | |
| 195 | 196 | clear(){ |
| 196 | - for(const r of [...this.#rows/*clone because #rows may change*/]){ | |
| 197 | + for(const r of [...this.#rows/*clone because this updates #rows*/]){ | |
| 197 | 198 | this.#removeRow(r); |
| 198 | 199 | } |
| 199 | 200 | this.reportError(); |
| 200 | 201 | } |
| 201 | 202 | |
| @@ -384,10 +385,14 @@ | ||
| 384 | 385 | } |
| 385 | 386 | |
| 386 | 387 | #injestBlob(rowObj, file){ |
| 387 | 388 | if( !file ) return; |
| 388 | 389 | const old = this.#rowMatchingName(file.name); |
| 390 | + if( rowObj.overrideName ){ | |
| 391 | + file = new File([file], rowObj.overrideName, {type: file.type}); | |
| 392 | + rowObj.overrideName = undefined; | |
| 393 | + } | |
| 389 | 394 | if( old && rowObj !== old ){ |
| 390 | 395 | /* |
| 391 | 396 | Fossil attachments treat the name as a unique-per-target |
| 392 | 397 | key, with the newest one being the primary. If a name is |
| 393 | 398 | given twice, remove the new entry and reuse the older |
| @@ -398,15 +403,11 @@ | ||
| 398 | 403 | attachment artifacts. |
| 399 | 404 | */ |
| 400 | 405 | /* recycle `old` instead to avoid UI flicker. */ |
| 401 | 406 | this.#rowError(old); |
| 402 | 407 | this.#removeRow(rowObj); |
| 403 | - rowObj.e = old.e; | |
| 404 | - } | |
| 405 | - if( rowObj.overrideName ){ | |
| 406 | - file = new File([file], rowObj.overrideName, {type: file.type}); | |
| 407 | - rowObj.overrideName = undefined; | |
| 408 | + rowObj = old; | |
| 408 | 409 | } |
| 409 | 410 | |
| 410 | 411 | let szLbl; |
| 411 | 412 | if( file.size < 500000 ){ |
| 412 | 413 | szLbl = file.size + ' bytes'; |
| @@ -540,16 +541,11 @@ | ||
| 540 | 541 | if( !li.length ) return; |
| 541 | 542 | if( eBtnSubmit.dataset.submitted ) return; |
| 542 | 543 | eBtnSubmit.dataset.submitted = 1; |
| 543 | 544 | D.disable(eBtnSubmit); |
| 544 | 545 | const fd = new FormData(); |
| 545 | - let i = 0; | |
| 546 | - for(const row of li){ | |
| 547 | - ++i; | |
| 548 | - fd.append('file'+i, row.content); | |
| 549 | - if( row.description ) fd.append('file'+i+'_desc', row.description); | |
| 550 | - } | |
| 546 | + att.populateFormData(fd); | |
| 551 | 547 | for( const eIn of eAttachWrapper.querySelectorAll( |
| 552 | 548 | 'input[type="hidden"]' |
| 553 | 549 | ) ){ |
| 554 | 550 | /* Copy over hidden input fields emitted by the server. */ |
| 555 | 551 | if( eIn.name==='target' ){ |
| @@ -581,15 +577,15 @@ | ||
| 581 | 577 | if( to ){ |
| 582 | 578 | if( '/'!==to[0] ){ |
| 583 | 579 | to = F.repoUrl(to); |
| 584 | 580 | } |
| 585 | 581 | window.location = to; |
| 586 | - }else if( target ){ | |
| 582 | + }else if( zTarget ){ | |
| 587 | 583 | window.location = '?target='+zTarget+'&'+Date.now(); |
| 588 | 584 | } |
| 589 | 585 | } |
| 590 | 586 | })/*submit handler*/; |
| 591 | 587 | updateBtnSubmit(att); |
| 592 | 588 | F.page.attacher = att /* only for testing via dev console */; |
| 593 | 589 | }/* /attachadd */ |
| 594 | 590 | |
| 595 | 591 | })(window.fossil); |
| 596 | 592 |
| --- src/fossil.attach.js | |
| +++ src/fossil.attach.js | |
| @@ -6,23 +6,27 @@ | |
| 6 | Requires that window.fossil has already been set up. |
| 7 | Depends on fossil.dom. |
| 8 | */ |
| 9 | (function(namespace){ |
| 10 | "use strict"; |
| 11 | const F = namespace, D = fossil.dom; |
| 12 | |
| 13 | let idCounter = 0; |
| 14 | |
| 15 | /** |
| 16 | Implements a multi-file selector widget. Intended to be plugged |
| 17 | in to places in Fossil's UI where attachments can be assigned to |
| 18 | an artifact. |
| 19 | */ |
| 20 | class Attacher { |
| 21 | #opt; |
| 22 | #rows = []; |
| 23 | #e = Object.create(null); |
| 24 | #events = new EventTarget(); |
| 25 | |
| 26 | /** |
| 27 | Options: |
| 28 | |
| @@ -147,11 +151,11 @@ | |
| 147 | get isDryRun(){ |
| 148 | return !!this.#opt.dryRun; |
| 149 | } |
| 150 | /** |
| 151 | Returns the DOM element (div.attach-controls) which wraps the |
| 152 | "Add" button. Clients may add buttons to it. |
| 153 | */ |
| 154 | get controlsElement(){ |
| 155 | return this.#e.controls; |
| 156 | } |
| 157 | |
| @@ -182,20 +186,17 @@ | |
| 182 | row: rowObj, |
| 183 | attacher: this |
| 184 | }) |
| 185 | }) |
| 186 | ); |
| 187 | if( false /* arguable */ |
| 188 | && 0===this.#rows.length |
| 189 | && this.#opt.startWith>0 ){ |
| 190 | /* Intended primarily for /addattach. */ |
| 191 | this.#addRow(); |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | clear(){ |
| 196 | for(const r of [...this.#rows/*clone because #rows may change*/]){ |
| 197 | this.#removeRow(r); |
| 198 | } |
| 199 | this.reportError(); |
| 200 | } |
| 201 | |
| @@ -384,10 +385,14 @@ | |
| 384 | } |
| 385 | |
| 386 | #injestBlob(rowObj, file){ |
| 387 | if( !file ) return; |
| 388 | const old = this.#rowMatchingName(file.name); |
| 389 | if( old && rowObj !== old ){ |
| 390 | /* |
| 391 | Fossil attachments treat the name as a unique-per-target |
| 392 | key, with the newest one being the primary. If a name is |
| 393 | given twice, remove the new entry and reuse the older |
| @@ -398,15 +403,11 @@ | |
| 398 | attachment artifacts. |
| 399 | */ |
| 400 | /* recycle `old` instead to avoid UI flicker. */ |
| 401 | this.#rowError(old); |
| 402 | this.#removeRow(rowObj); |
| 403 | rowObj.e = old.e; |
| 404 | } |
| 405 | if( rowObj.overrideName ){ |
| 406 | file = new File([file], rowObj.overrideName, {type: file.type}); |
| 407 | rowObj.overrideName = undefined; |
| 408 | } |
| 409 | |
| 410 | let szLbl; |
| 411 | if( file.size < 500000 ){ |
| 412 | szLbl = file.size + ' bytes'; |
| @@ -540,16 +541,11 @@ | |
| 540 | if( !li.length ) return; |
| 541 | if( eBtnSubmit.dataset.submitted ) return; |
| 542 | eBtnSubmit.dataset.submitted = 1; |
| 543 | D.disable(eBtnSubmit); |
| 544 | const fd = new FormData(); |
| 545 | let i = 0; |
| 546 | for(const row of li){ |
| 547 | ++i; |
| 548 | fd.append('file'+i, row.content); |
| 549 | if( row.description ) fd.append('file'+i+'_desc', row.description); |
| 550 | } |
| 551 | for( const eIn of eAttachWrapper.querySelectorAll( |
| 552 | 'input[type="hidden"]' |
| 553 | ) ){ |
| 554 | /* Copy over hidden input fields emitted by the server. */ |
| 555 | if( eIn.name==='target' ){ |
| @@ -581,15 +577,15 @@ | |
| 581 | if( to ){ |
| 582 | if( '/'!==to[0] ){ |
| 583 | to = F.repoUrl(to); |
| 584 | } |
| 585 | window.location = to; |
| 586 | }else if( target ){ |
| 587 | window.location = '?target='+zTarget+'&'+Date.now(); |
| 588 | } |
| 589 | } |
| 590 | })/*submit handler*/; |
| 591 | updateBtnSubmit(att); |
| 592 | F.page.attacher = att /* only for testing via dev console */; |
| 593 | }/* /attachadd */ |
| 594 | |
| 595 | })(window.fossil); |
| 596 |
| --- src/fossil.attach.js | |
| +++ src/fossil.attach.js | |
| @@ -6,23 +6,27 @@ | |
| 6 | Requires that window.fossil has already been set up. |
| 7 | Depends on fossil.dom. |
| 8 | */ |
| 9 | (function(namespace){ |
| 10 | "use strict"; |
| 11 | const F = namespace, D = F.dom; |
| 12 | |
| 13 | let idCounter = 0; |
| 14 | |
| 15 | /** |
| 16 | Implements a multi-file selector widget. Intended to be plugged |
| 17 | in to places in Fossil's UI where attachments can be assigned to |
| 18 | an artifact. |
| 19 | */ |
| 20 | class Attacher { |
| 21 | /* Options. */ |
| 22 | #opt; |
| 23 | /* List of objects representing each row. */ |
| 24 | #rows = []; |
| 25 | /* DOM elements */ |
| 26 | #e = Object.create(null); |
| 27 | /* Proxy for various events this object fires. */ |
| 28 | #events = new EventTarget(); |
| 29 | |
| 30 | /** |
| 31 | Options: |
| 32 | |
| @@ -147,11 +151,11 @@ | |
| 151 | get isDryRun(){ |
| 152 | return !!this.#opt.dryRun; |
| 153 | } |
| 154 | /** |
| 155 | Returns the DOM element (div.attach-controls) which wraps the |
| 156 | "Add" button. Clients may add buttons to it. |
| 157 | */ |
| 158 | get controlsElement(){ |
| 159 | return this.#e.controls; |
| 160 | } |
| 161 | |
| @@ -182,20 +186,17 @@ | |
| 186 | row: rowObj, |
| 187 | attacher: this |
| 188 | }) |
| 189 | }) |
| 190 | ); |
| 191 | } |
| 192 | |
| 193 | /** |
| 194 | Removes all attachments and clears the error state. |
| 195 | */ |
| 196 | clear(){ |
| 197 | for(const r of [...this.#rows/*clone because this updates #rows*/]){ |
| 198 | this.#removeRow(r); |
| 199 | } |
| 200 | this.reportError(); |
| 201 | } |
| 202 | |
| @@ -384,10 +385,14 @@ | |
| 385 | } |
| 386 | |
| 387 | #injestBlob(rowObj, file){ |
| 388 | if( !file ) return; |
| 389 | const old = this.#rowMatchingName(file.name); |
| 390 | if( rowObj.overrideName ){ |
| 391 | file = new File([file], rowObj.overrideName, {type: file.type}); |
| 392 | rowObj.overrideName = undefined; |
| 393 | } |
| 394 | if( old && rowObj !== old ){ |
| 395 | /* |
| 396 | Fossil attachments treat the name as a unique-per-target |
| 397 | key, with the newest one being the primary. If a name is |
| 398 | given twice, remove the new entry and reuse the older |
| @@ -398,15 +403,11 @@ | |
| 403 | attachment artifacts. |
| 404 | */ |
| 405 | /* recycle `old` instead to avoid UI flicker. */ |
| 406 | this.#rowError(old); |
| 407 | this.#removeRow(rowObj); |
| 408 | rowObj = old; |
| 409 | } |
| 410 | |
| 411 | let szLbl; |
| 412 | if( file.size < 500000 ){ |
| 413 | szLbl = file.size + ' bytes'; |
| @@ -540,16 +541,11 @@ | |
| 541 | if( !li.length ) return; |
| 542 | if( eBtnSubmit.dataset.submitted ) return; |
| 543 | eBtnSubmit.dataset.submitted = 1; |
| 544 | D.disable(eBtnSubmit); |
| 545 | const fd = new FormData(); |
| 546 | att.populateFormData(fd); |
| 547 | for( const eIn of eAttachWrapper.querySelectorAll( |
| 548 | 'input[type="hidden"]' |
| 549 | ) ){ |
| 550 | /* Copy over hidden input fields emitted by the server. */ |
| 551 | if( eIn.name==='target' ){ |
| @@ -581,15 +577,15 @@ | |
| 577 | if( to ){ |
| 578 | if( '/'!==to[0] ){ |
| 579 | to = F.repoUrl(to); |
| 580 | } |
| 581 | window.location = to; |
| 582 | }else if( zTarget ){ |
| 583 | window.location = '?target='+zTarget+'&'+Date.now(); |
| 584 | } |
| 585 | } |
| 586 | })/*submit handler*/; |
| 587 | updateBtnSubmit(att); |
| 588 | F.page.attacher = att /* only for testing via dev console */; |
| 589 | }/* /attachadd */ |
| 590 | |
| 591 | })(window.fossil); |
| 592 |