Fossil SCM
Implement Javascript-based keyboard navigation for web UI diffs. Shortcuts SHIFT+I or I show or hide all diffs, and P or O show the next or previous diff. See [forum:a78f44576c| Forum Post a78f44576c] for more information.
Commit
b7e089e0f3ef8109b45febcd11a2b03af952d58db6c69e3eb5c60c7b4e8f63a1
Parent
83bc81e88296200…
1 file changed
+94
+94
| --- src/fossil.diff.js | ||
| +++ src/fossil.diff.js | ||
| @@ -18,10 +18,104 @@ | ||
| 18 | 18 | }, false); |
| 19 | 19 | }; |
| 20 | 20 | document.querySelectorAll('table.diff').forEach(addToggle); |
| 21 | 21 | }); |
| 22 | 22 | |
| 23 | +/* | |
| 24 | +** Diff keyboard navigation shortcuts: | |
| 25 | +** | |
| 26 | +** SHIFT+I - Show all diffs. | |
| 27 | +** I - Hide all diffs. | |
| 28 | +** P - Show only next diff, hide all others. | |
| 29 | +** O - Show only previous diff, hide all others. | |
| 30 | +** | |
| 31 | +** Ideas and TODOs: | |
| 32 | +** | |
| 33 | +** o Documentation. | |
| 34 | +** o Restore shown/hidden state on back/forward navigation (or simply reset | |
| 35 | +** shown/hidden state to show all). | |
| 36 | +*/ | |
| 37 | +(function(){ | |
| 38 | + window.addEventListener('load',function(){ | |
| 39 | + function btnScrollIntoView(e){ | |
| 40 | + e = e.parentElement; | |
| 41 | + var rc = e.getBoundingClientRect(); | |
| 42 | + var y = 0; | |
| 43 | + do{ | |
| 44 | + y += e.offsetTop; | |
| 45 | + }while( e = e.offsetParent ); | |
| 46 | + window.scrollTo(0,y-6*rc.height); | |
| 47 | + } | |
| 48 | + document.addEventListener('keydown',function(evt){ | |
| 49 | + //if( evt.target.tagName=='INPUT' || evt.target.tagName=='SELECT' ) return; | |
| 50 | + var | |
| 51 | + mSHIFT = 1<<13, | |
| 52 | + kSHOW = mSHIFT | 73 /* SHIFT+I */, | |
| 53 | + kHIDE = 73 /* I */, | |
| 54 | + kNEXT = 80 /* P */, | |
| 55 | + kPREV = 79 /* O */, | |
| 56 | + mod = evt.altKey<<15 | evt.ctrlKey<<14 | evt.shiftKey<<13, | |
| 57 | + key = ( evt.which || evt.keyCode ) | mod; | |
| 58 | + switch( key ){ | |
| 59 | + case kSHOW: | |
| 60 | + case kHIDE: | |
| 61 | + case kNEXT: | |
| 62 | + case kPREV: break; | |
| 63 | + default: return; | |
| 64 | + } | |
| 65 | + evt.preventDefault(); | |
| 66 | + evt.stopPropagation(); | |
| 67 | + if( key==kSHOW || key==kHIDE ){ | |
| 68 | + var btn = document.getElementsByClassName('diff-toggle'); | |
| 69 | + if( btn.length>0 ){ | |
| 70 | + var chg = 0; | |
| 71 | + for( var i=0; i<btn.length; i++ ){ | |
| 72 | + if( btn[i].checked && key==kHIDE ){ | |
| 73 | + btn[i].click(); | |
| 74 | + chg++; | |
| 75 | + } | |
| 76 | + else if( !btn[i].checked && key==kSHOW ){ | |
| 77 | + btn[i].click(); | |
| 78 | + chg++; | |
| 79 | + } | |
| 80 | + } | |
| 81 | + if( chg>0 ) btnScrollIntoView(btn[0]); | |
| 82 | + } | |
| 83 | + } | |
| 84 | + else if( key==kNEXT || key==kPREV ){ | |
| 85 | + var btn = document.getElementsByClassName('diff-toggle'); | |
| 86 | + if( btn.length>1 ){ | |
| 87 | + var nFolded = 0, n = -2; | |
| 88 | + for( var i=0; i<btn.length; i++ ){ | |
| 89 | + if( !btn[i].checked ) nFolded++; | |
| 90 | + } | |
| 91 | + if( nFolded==0 ){ | |
| 92 | + n = ( key==kNEXT ? 0 : btn.length-1 ); | |
| 93 | + for( var i=0; i<btn.length; i++ ){ | |
| 94 | + if( n!=i ) btn[i].click(); | |
| 95 | + } | |
| 96 | + } | |
| 97 | + else{ | |
| 98 | + for( var i=0; i<btn.length; i++ ){ | |
| 99 | + if( btn[i].checked ){ | |
| 100 | + if( n==-2 ) n = ( key==kNEXT ? i+1 : i-1 ); | |
| 101 | + if( n!=i ) btn[i].click(); | |
| 102 | + } | |
| 103 | + } | |
| 104 | + } | |
| 105 | + if( n==-2 ) n = ( key==kNEXT ? 0 : btn.length-1 ); | |
| 106 | + if( n in btn ){ | |
| 107 | + if( !btn[n].checked ) btn[n].click(); | |
| 108 | + btnScrollIntoView(btn[n]); | |
| 109 | + } | |
| 110 | + } | |
| 111 | + else btn[0].click(); | |
| 112 | + } | |
| 113 | + }/*,true*/); | |
| 114 | + },false); | |
| 115 | +}()); | |
| 116 | + | |
| 23 | 117 | window.fossil.onPageLoad(function(){ |
| 24 | 118 | const F = window.fossil, D = F.dom; |
| 25 | 119 | const Diff = F.diff = { |
| 26 | 120 | e:{/*certain cached DOM elements*/}, |
| 27 | 121 | config: { |
| 28 | 122 |
| --- src/fossil.diff.js | |
| +++ src/fossil.diff.js | |
| @@ -18,10 +18,104 @@ | |
| 18 | }, false); |
| 19 | }; |
| 20 | document.querySelectorAll('table.diff').forEach(addToggle); |
| 21 | }); |
| 22 | |
| 23 | window.fossil.onPageLoad(function(){ |
| 24 | const F = window.fossil, D = F.dom; |
| 25 | const Diff = F.diff = { |
| 26 | e:{/*certain cached DOM elements*/}, |
| 27 | config: { |
| 28 |
| --- src/fossil.diff.js | |
| +++ src/fossil.diff.js | |
| @@ -18,10 +18,104 @@ | |
| 18 | }, false); |
| 19 | }; |
| 20 | document.querySelectorAll('table.diff').forEach(addToggle); |
| 21 | }); |
| 22 | |
| 23 | /* |
| 24 | ** Diff keyboard navigation shortcuts: |
| 25 | ** |
| 26 | ** SHIFT+I - Show all diffs. |
| 27 | ** I - Hide all diffs. |
| 28 | ** P - Show only next diff, hide all others. |
| 29 | ** O - Show only previous diff, hide all others. |
| 30 | ** |
| 31 | ** Ideas and TODOs: |
| 32 | ** |
| 33 | ** o Documentation. |
| 34 | ** o Restore shown/hidden state on back/forward navigation (or simply reset |
| 35 | ** shown/hidden state to show all). |
| 36 | */ |
| 37 | (function(){ |
| 38 | window.addEventListener('load',function(){ |
| 39 | function btnScrollIntoView(e){ |
| 40 | e = e.parentElement; |
| 41 | var rc = e.getBoundingClientRect(); |
| 42 | var y = 0; |
| 43 | do{ |
| 44 | y += e.offsetTop; |
| 45 | }while( e = e.offsetParent ); |
| 46 | window.scrollTo(0,y-6*rc.height); |
| 47 | } |
| 48 | document.addEventListener('keydown',function(evt){ |
| 49 | //if( evt.target.tagName=='INPUT' || evt.target.tagName=='SELECT' ) return; |
| 50 | var |
| 51 | mSHIFT = 1<<13, |
| 52 | kSHOW = mSHIFT | 73 /* SHIFT+I */, |
| 53 | kHIDE = 73 /* I */, |
| 54 | kNEXT = 80 /* P */, |
| 55 | kPREV = 79 /* O */, |
| 56 | mod = evt.altKey<<15 | evt.ctrlKey<<14 | evt.shiftKey<<13, |
| 57 | key = ( evt.which || evt.keyCode ) | mod; |
| 58 | switch( key ){ |
| 59 | case kSHOW: |
| 60 | case kHIDE: |
| 61 | case kNEXT: |
| 62 | case kPREV: break; |
| 63 | default: return; |
| 64 | } |
| 65 | evt.preventDefault(); |
| 66 | evt.stopPropagation(); |
| 67 | if( key==kSHOW || key==kHIDE ){ |
| 68 | var btn = document.getElementsByClassName('diff-toggle'); |
| 69 | if( btn.length>0 ){ |
| 70 | var chg = 0; |
| 71 | for( var i=0; i<btn.length; i++ ){ |
| 72 | if( btn[i].checked && key==kHIDE ){ |
| 73 | btn[i].click(); |
| 74 | chg++; |
| 75 | } |
| 76 | else if( !btn[i].checked && key==kSHOW ){ |
| 77 | btn[i].click(); |
| 78 | chg++; |
| 79 | } |
| 80 | } |
| 81 | if( chg>0 ) btnScrollIntoView(btn[0]); |
| 82 | } |
| 83 | } |
| 84 | else if( key==kNEXT || key==kPREV ){ |
| 85 | var btn = document.getElementsByClassName('diff-toggle'); |
| 86 | if( btn.length>1 ){ |
| 87 | var nFolded = 0, n = -2; |
| 88 | for( var i=0; i<btn.length; i++ ){ |
| 89 | if( !btn[i].checked ) nFolded++; |
| 90 | } |
| 91 | if( nFolded==0 ){ |
| 92 | n = ( key==kNEXT ? 0 : btn.length-1 ); |
| 93 | for( var i=0; i<btn.length; i++ ){ |
| 94 | if( n!=i ) btn[i].click(); |
| 95 | } |
| 96 | } |
| 97 | else{ |
| 98 | for( var i=0; i<btn.length; i++ ){ |
| 99 | if( btn[i].checked ){ |
| 100 | if( n==-2 ) n = ( key==kNEXT ? i+1 : i-1 ); |
| 101 | if( n!=i ) btn[i].click(); |
| 102 | } |
| 103 | } |
| 104 | } |
| 105 | if( n==-2 ) n = ( key==kNEXT ? 0 : btn.length-1 ); |
| 106 | if( n in btn ){ |
| 107 | if( !btn[n].checked ) btn[n].click(); |
| 108 | btnScrollIntoView(btn[n]); |
| 109 | } |
| 110 | } |
| 111 | else btn[0].click(); |
| 112 | } |
| 113 | }/*,true*/); |
| 114 | },false); |
| 115 | }()); |
| 116 | |
| 117 | window.fossil.onPageLoad(function(){ |
| 118 | const F = window.fossil, D = F.dom; |
| 119 | const Diff = F.diff = { |
| 120 | e:{/*certain cached DOM elements*/}, |
| 121 | config: { |
| 122 |