Fossil SCM

fossil-scm / src / fossil.numbered-lines.js
Source Blame History 120 lines
b699040… drh 1 (function callee(arg){
b699040… drh 2 /*
b699040… drh 3 JS counterpart of info.c:output_text_with_line_numbers()
b699040… drh 4 which ties an event handler to the line numbers to allow
b699040… drh 5 selection of individual lines or ranges.
b699040… drh 6
b699040… drh 7 Requires: fossil.bootstrap, fossil.dom, fossil.popupwidget,
b699040… drh 8 fossil.copybutton
b699040… drh 9 */
b699040… drh 10 var tbl = arg || document.querySelectorAll('table.numbered-lines');
34f7fd7… stephan 11 if(tbl && !arg){
b699040… drh 12 if(tbl.length>1){ /* multiple query results: recurse */
b699040… drh 13 tbl.forEach( (t)=>callee(t) );
b699040… drh 14 return;
b699040… drh 15 }else{/* single query result */
b699040… drh 16 tbl = tbl[0];
b699040… drh 17 }
b699040… drh 18 }
34f7fd7… stephan 19 if(!tbl) return /* no matching elements */;
b699040… drh 20 const F = window.fossil, D = F.dom;
b699040… drh 21 const tdLn = tbl.querySelector('td.line-numbers');
8981518… george 22 const urlArgsRaw = (window.location.search||'?')
7c98df4… stephan 23 .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
7c98df4… stephan 24 .replace(/&?\bln=[^&]*/,'') /* inbound line number/range */
8981518… george 25 .replace('?&','?');
7641c82… stephan 26 const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 };
0f03f78… wyoung 27 const lineTip = new F.PopupWidget({
b699040… drh 28 refresh: function(){
b699040… drh 29 const link = this.state.link;
b699040… drh 30 D.clearElement(link);
b699040… drh 31 if(lineState.start){
b699040… drh 32 const ls = [lineState.start];
b699040… drh 33 if(lineState.end) ls.push(lineState.end);
b699040… drh 34 link.dataset.url = (
b699040… drh 35 window.location.toString().split('?')[0]
b699040… drh 36 + lineState.urlArgs + '&ln='+ls.join('-')
b699040… drh 37 );
b699040… drh 38 D.append(
b699040… drh 39 D.clearElement(link),
7c98df4… stephan 40 ' Copy link to '+(
7c98df4… stephan 41 ls.length===1 ? 'line ' : 'lines '
7c98df4… stephan 42 )+ls.join('-')
b699040… drh 43 );
b699040… drh 44 }else{
b699040… drh 45 D.append(link, "No lines selected.");
b699040… drh 46 }
b699040… drh 47 },
b699040… drh 48 init: function(){
b699040… drh 49 const e = this.e;
63712b6… florian 50 const btnCopy = D.attr(D.button(), 'id', 'linenum-copy-button');
63712b6… florian 51 link = D.label('linenum-copy-button');
b699040… drh 52 this.state = {link};
b699040… drh 53 F.copyButton(btnCopy,{
b699040… drh 54 copyFromElement: link,
b699040… drh 55 extractText: ()=>link.dataset.url,
b699040… drh 56 oncopy: (ev)=>{
63712b6… florian 57 setTimeout(()=>lineTip.hide(), 400);
7c98df4… stephan 58 // arguably too snazzy: F.toast.message("Copied link to clipboard.");
b699040… drh 59 }
b699040… drh 60 });
63712b6… florian 61 D.append(this.e, btnCopy, link);
b699040… drh 62 }
b699040… drh 63 });
b699040… drh 64
b699040… drh 65 tbl.addEventListener('click', ()=>lineTip.hide(), true);
b699040… drh 66
b699040… drh 67 tdLn.addEventListener('click', function f(ev){
b699040… drh 68 if('SPAN'!==ev.target.tagName) return;
b699040… drh 69 else if('number' !== typeof f.mode){
b699040… drh 70 f.mode = 0 /*0=none selected, 1=1 selected, 2=2 selected*/;
b699040… drh 71 f.spans = tdLn.querySelectorAll('span');
b699040… drh 72 f.selected = tdLn.querySelectorAll('span.selected-line');
b699040… drh 73 f.unselect = (e)=>D.removeClass(e, 'selected-line','start','end');
b699040… drh 74 }
b699040… drh 75 ev.stopPropagation();
b699040… drh 76 const ln = +ev.target.innerText;
b699040… drh 77 if(2===f.mode){/*Reset selection*/
b699040… drh 78 f.mode = 0;
b699040… drh 79 }
b699040… drh 80 if(0===f.mode){/*Select single line*/
b699040… drh 81 lineState.end = 0;
b699040… drh 82 lineState.start = ln;
b699040… drh 83 f.mode = 1;
b699040… drh 84 }else if(1===f.mode){
b699040… drh 85 if(ln === lineState.start){/*Unselect line*/
b699040… drh 86 lineState.start = 0;
b699040… drh 87 f.mode = 0;
b699040… drh 88 }else{/*Select range*/
b699040… drh 89 if(ln<lineState.start){
b699040… drh 90 lineState.end = lineState.start;
b699040… drh 91 lineState.start = ln;
b699040… drh 92 }else{
b699040… drh 93 lineState.end = ln;
b699040… drh 94 }
b699040… drh 95 f.mode = 2;
b699040… drh 96 }
b699040… drh 97 }
b699040… drh 98 if(f.selected){/*Unmark previously-selected lines.*/
b699040… drh 99 f.selected.forEach(f.unselect);
b699040… drh 100 f.selected = undefined;
b699040… drh 101 }
b699040… drh 102 if(0===f.mode){
b699040… drh 103 lineTip.hide();
b699040… drh 104 }else{/*Mark selected lines*/
b699040… drh 105 const rect = ev.target.getBoundingClientRect();
b699040… drh 106 f.selected = [];
b699040… drh 107 if(f.spans.length>=lineState.start){
b699040… drh 108 let i = lineState.start, end = lineState.end || lineState.start, span = f.spans[i-1];
b699040… drh 109 for( ; i<=end && span; span = f.spans[i++] ){
b699040… drh 110 span.classList.add('selected-line');
b699040… drh 111 f.selected.push(span);
b699040… drh 112 if(i===lineState.start) span.classList.add('start');
b699040… drh 113 if(i===end) span.classList.add('end');
b699040… drh 114 }
b699040… drh 115 }
b699040… drh 116 lineTip.refresh().show(rect.right+3, rect.top-4);
b699040… drh 117 }
b699040… drh 118 }, false);
b699040… drh 119
b699040… drh 120 })();

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button